2-1. 文字列 (string)

文書処理などに必要な文字列について説明します。

参考

Pythonが扱うデータには様々な種類がありますが、文字列はいくつかの文字の並びから構成されるデータです。 Pythonは標準で多言語に対応しており、 英語アルファベットだけではなく日本語をはじめとする多くの言語を取り扱えます。

文字列は、文字の並びをシングルクォート '...'、もしくはダブルクォート "..." で囲んで記述します。

以下の例では文字列をそれぞれ、変数 word1, word2 に代入しています。

[1]:
word1 = 'hello'
word1
[1]:
'hello'
[2]:
word2 = 'Hello'
word2
[2]:
'Hello'

上の変数の値が確かに文字列であることは、組み込み関数 type によって確認できます。 type は、任意のデータを引数として、そのデータの種類を返します。 データの種類は、データ型もしくはと呼ばれます。

[3]:
type(word1)
[3]:
str

str は文字列のデータ型を意味します。

[4]:
type(word2)
[4]:
str

str は組み込み関数としても用いられます。 組み込み関数 str を使えば、任意のデータを文字列に変換できます。 一般に、データ型は、そのデータ型への変換を行う関数として用いられることが多いです。

1-1で学んだ数値を文字列に変換したい場合、次のように行います。

[5]:
word3 = str(123)
word3
[5]:
'123'

逆に、'123' という文字列を整数に変換するには、1-1で述べた int という関数を用いることができます。 (実は int は整数の型でもあります。)

[6]:
i = int('123')
i
[6]:
123

関数 float を用いれば文字列を実数に変換できます。

[7]:
f = float('123.4')
f
[7]:
123.4

文字列の長さは、組み込み関数 len を用いて次のようにして求めます。

[8]:
len(word1)
[8]:
5

複数行にわたる文字列を記述するには、トリプルクォート('''...''' もしくは """...""")を用いることができます。 上記の参考URLを参照してください。 トリプルクォートはコメントとしても用いられます。 なお、1-4 のコーディングスタイルのところで紹介したスタイルガイドのPEP8では、 トリプルクォートには """...""" を使うのが適切と定められています。

文字列とインデックス

文字列はいくつかの文字によって構成されています。

文字列 'hello' の3番目の文字を得たい場合は、以下のような記法を用います。

[9]:
'hello'[2]
[9]:
'l'

文字列を値とする変数に対しても同様の記法を用います。多くの場合は変数に対してこの記法を用います。

[10]:
word1 = 'hello'
word1[2]
[10]:
'l'

この括弧内の数値のことをインデックスと呼びます。インデックスは 0 から始まるので、 ある文字列の x 番目の要素を得るには、インデックスとして x-1 を指定する必要があります。

こうして取得した文字は、Pythonでは長さが 1 の文字列として扱われます。 (プログラミング言語によっては、文字列ではなく別の型のデータとして扱われるものもありますので注意してください。)

文字列に対して、インデックスを指定してその要素を変更することはできません。(次のセルはエラーとなります) Pythonのデータは、大きく、変更可能なものと変更不可能なものに分類できますが、 文字列は変更不可能なデータです。 したがって、文字列を加工する場合は、新たに別の文字列を作成します。

[11]:
word1 = 'hello'
word1[0] = 'H'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/tmp/ipykernel_1788/1689567903.py in <module>
      1 word1 = 'hello'
----> 2 word1[0] = 'H'

TypeError: 'str' object does not support item assignment

また、文字列の長さ以上のインデックスを指定することはできません。(次はエラーとなります)

[12]:
word1[100]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/tmp/ipykernel_1788/521024611.py in <module>
----> 1 word1[100]

IndexError: string index out of range

インデックスに負数を指定すると、 文字列を後ろから数えた順序に従って文字列を構成する文字を得ます。 たとえば、文字列の最後の文字を取得するには、-1 を指定します。

[13]:
word1[-1]
[13]:
'o'

まとめると文字列 hello の正負のインデックスは以下の表の関係になります。

インデックス

h

e

l

l

o

0か正

0

1

2

3

4

-5

-4

-3

-2

-1

文字列とスライス

スライスと呼ばれる機能を利用して、文字列の一部(部分文字列)を取得できます。

具体的には、取得したい部分文字列の先頭の文字のインデックスと最後の文字のインデックスに 1 を加えた値を指定します。 たとえば、ある文字列の2番目の文字から4番目までの文字の部分文字列を得るには次のようにします。

[14]:
digits1='0123456789'
digits1[1:4]
[14]:
'123'

文字列の先頭(すなわち、インデックスが 0 の文字)を指定する場合、次のように行えます。

[15]:
digits1[0:3]
[15]:
'012'

しかし、最初の 0 は省略しても同じ結果となります。

[16]:
digits1[:3]
[16]:
'012'

同様に、最後尾の文字のインデックスも、値を省略することもできます。

[17]:
digits1[3:]
[17]:
'3456789'
[18]:
digits1[3:5]
[18]:
'34'

スライスにおいても負数を指定して、 文字列の最後の方から部分文字列を取得できます。

[19]:
digits1[-4:-1]
[19]:
'678'

スライスでは3番目の値を指定することで、とびとびの文字を指定できます。次のように digits1[3:9:2] と指定すると、インデックス 3 から2文字おきにインデックス 9 より小さい文字を並べた部分文字列を得ます。

[20]:
digits1[3:9:2]
[20]:
'357'

3番目の値に -1 を指定することもできます。これを使えば元の文字列の逆向きの文字列を得ることができます。

[21]:
digits1[8:4:-1]
[21]:
'8765'

空文字列

シングルクォート(もしくはダブルクォート)で、何も囲まない場合、長さ 0 の文字列(空文字列(くうもじれつ)もしくは、空列(くうれつ))となります。 具体的には、下記のように使用します。

blank = ''

空文字列は、次のように、たとえば文字列中からある部分文字列を取り除くのに使用します。 (replace は後で説明します。)

[22]:
price = '2,980円'
price.replace(',', '')
[22]:
'2980円'

文字列のスライスにおいて、指定したインデックスの範囲に文字列が存在しない場合、 たとえば、最初に指定したインデックス x に対して、 2番目のインデックスの値に x 以下のインデックスの値を指定するとどうなるでしょうか? (ただし、2つのインデックスは同じ符号を持つとし、スライスの3番目の値は用いないとします。) このような場合、結果は次のように空文字列となります(エラーが出たり、結果が None にはならないことに注意してください)。

[23]:
digits1='0123456789'
print('空文字列1 = ', digits1[4:2])
print('空文字列2 = ', digits1[-1:-4])
print('空文字列3 = ', digits1[3:3])
print('空文字列ではない = ', digits1[3:-1])
空文字列1 =
空文字列2 =
空文字列3 =
空文字列ではない =  345678

文字列の検索

文字列A文字列B を含むかどうかを調べるには、in 演算子を使います。 具体的には、次のように使用します。

文字列B in 文字列A

調べたい 文字列B が含まれていれば True が、そうでなければ False が返ります。

[24]:
'lo' in 'hello'
[24]:
True
[25]:
'z' in 'hello'
[25]:
False

実際のプログラムでは文字列を値とする変数を用いることが多いでしょう。

[26]:
word1 =  'hello'
substr1 = 'lo'
substr1 in word1
[26]:
True
[27]:
substr2 = 'z'
substr2 in word1
[27]:
False

not in 演算子は、in 演算子の逆を意味します。

[28]:
word1 =  'hello'
substr2 = 'z'
substr2 not in word1
[28]:
True

▲エスケープシーケンス

文字列を作成するにはシングル ' あるいはダブルクォート " で囲むと説明しました。 これらの文字を含む文字列を作成するには、エスケープシーケンスと呼ばれる特殊な文字列を使う必要があります。

たとえば、下のように文字列に ' を含む文字列を ' で囲むと文字列の範囲がずれてエラーとなります。

[29]:
non_escaped = 'This is 'MINE''
non_escaped
  File "/tmp/ipykernel_1788/1163335816.py", line 1
    non_escaped = 'This is 'MINE''
                            ^
SyntaxError: invalid syntax

エラーを避けるには、エスケープシーケンスで ' を記述します、具体的には ' の前に \ と記述すると、' を含む文字列を作成できます。

[30]:
escaped1 = 'This is \'MINE\''
escaped1
[30]:
"This is 'MINE'"

実は、シングルクォートで囲む代わりにダブルクォートを使えばエスケープシーケンスを使わずに記述できます。

[31]:
doublequated = "This is 'MINE'"
doublequated
[31]:
"This is 'MINE'"

他にも、ダブルクォートを表す \"\ を表す \\ 、改行を表す \n など、様々なエスケープシーケンスがあります。

[32]:
escaped2 = "時は金なり\n\"Time is money\"\nTime is \\"
print(escaped2)
時は金なり
"Time is money"
Time is \

3連のシングルクォート、もしくはダブルクォートを利用すれば、 \"\n を使わずに記述できます。

[33]:
triple_single_quated = '''時は金なり
'Time is money'
Time is \\'''
print(triple_single_quated)
時は金なり
'Time is money'
Time is \
[34]:
triple_double_quated = """時は金なり
'Time is money'
Time is \\"""
print(triple_single_quated)
時は金なり
'Time is money'
Time is \

なお、プログラムの一部を無効に(コメントアウト)したいとき、 3連のクォートで囲んで文字列にしてしまうことがあります。

バックスラッシュの表示と入力

エスケープシーケンスの先頭にある文字は、バックスラッシュ \(Unicode U+005C)です。 これはPythonに限った話ではないですが、バックスラッシュは環境(正確にはフォント)によって見え方が異なります。 Windows上のフォントでは、円記号 ¥ として見えることが多いです。 macOS上のフォントでは、そのままバックスラッシュとして見えることが多いです。

JIS配列キーボードでは、バックスラッシュキーがないことがあります。 Windows上では、円記号 ¥ キーでバックスラッシュが入力できます。 macOS上では、Alt + ¥ キーでバックスラッシュが入力できます。 ただし、IME設定によっても入力方法は変わるので注意してください。

[35]:
print('\n') # 改行文字(バックスラッシュ + n)
print('¥n') # 改行文字でない(円記号 + n)
print('⧵n') # 改行文字でない(Unicode U+29F5 のバックスラッシュ演算子 + n)


¥n
⧵n

文字列の連結

+ 演算子を用いれば文字列同士を連結できます。 この演算では新しい文字列が作られ、元の文字列は変化しません。

[36]:
word1 = 'hello'
word2 = ' world'
text1 = word1 + word2
text1
[36]:
'hello world'

* 演算子で文字列の繰り返し回数を指定できます。

[37]:
word1 = 'hello'
word1 * 3
[37]:
'hellohellohello'

文字列とメソッド

文字列に対する操作を行うため、様々なメソッド(関数のようなもの)が用意されています。

メソッドは必要に応じて (...) 内に引数を与え、以下のように使用します。

文字列.メソッド名(式, ...)
# あるいは
文字列変数.メソッド名(式, ...)

文字列には以下のようなメソッドが用意されています。

置換

replace メソッドは、指定した 部分文字列A を、別に指定した 文字列B で置き換えた文字列を作成します。 この操作では、元の文字列は変化しません。具体的には、次のように使用します。

文字列.replace(部分文字列A, 文字列B)
[38]:
word1 = 'hello'
word1.replace('l', '123')
[38]:
'he123123o'
[39]:
word1
[39]:
'hello'

練習

英語の文章からなる文字列 str_engsentences が引数として与えられたとき、str_engsentences 中に含まれる全ての句読点(., ,, :, ;, !, ?)を削除した文字列を返す関数 remove_punctuations を作成してください。 (練習の解答はこのノートブックの一番最後にあります。)

次のセルの ... のところを書き換えて remove_punctuations(str_engsentences) を作成してください。

[40]:
def remove_punctuations(str_engsentences):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が True になることを確認してください。

[41]:
print(remove_punctuations('Quiet, uh, donations, you want me to make a donation to the coast guard youth auxiliary?') == 'Quiet uh donations you want me to make a donation to the coast guard youth auxiliary')
False

練習

ATGCの4種類の文字から成る文字列 str_atgc が引数として与えられたとき、文字列 str_pair を返す関数 atgc_bppair を作成してください。ただし、str_pair は、str_atgc 中の各文字列に対して、 AT に、TA に、GC に、CG に置き換えたものです。

次のセルの ... のところを書き換えて atgc_bppair(str_atgc) を作成してください。

[42]:
def atgc_bppair(str_atgc):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が True になることを確認してください。

[43]:
print(atgc_bppair('AAGCCCCATGGTAA') == 'TTCGGGGTACCATT')
False

分割

split メソッドは、指定した 区切り文字列B で、文字列A を分割して、リストと呼ばれるデータに格納します。具体的には、次のように使用します。

文字列A.split(区切り文字列B)

リストについては 2-2 で扱います。

[44]:
fruits1 = 'apple,banana,cherry'
fruits1.split(',')
[44]:
['apple', 'banana', 'cherry']

検索

index メソッドにより、指定した 部分文字列B文字列A のどこに存在するか調べることができます。具体的には、次のように使用します。

文字列A.index(部分文字列B)

ただし、指定した部分文字列が文字列に複数回含まれる場合、最初のインデックスが返されます。また、指定した部分文字列が文字列に含まれない場合は、エラーとなります。

[45]:
word1 = 'hello'
word1.index('lo')
[45]:
3
[46]:
word1.index('l')
[46]:
2

以下はエラーとなります。

[47]:
word1.index('a')
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/tmp/ipykernel_1788/1197463809.py in <module>
----> 1 word1.index('a')

ValueError: substring not found

find メソッドも index と同様に部分文字列を検索し、最初に出現するインデックスを返します。

index との違いは、部分文字列が含まれない場合エラーとはならず -1 が返されることです。

[48]:
word1 = 'hello'
word1.find('a')
[48]:
-1

練習

コロン (:) を1つだけ含む文字列 str1 を引数として与えると、コロンの左右に存在する文字列を入れ替えた文字列を返す関数 swap_colon(str1) を作成してください。

次のセルの ... のところを書き換えて swap_colon(str1) を作成してください。

[49]:
def swap_colon(str1):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が True になることを確認してください。

[50]:
print(swap_colon('hello:world') == 'world:hello')
False

数え上げ

count メソッドにより、指定した 部分文字列B文字列A にいくつ存在するか調べることができます。

文字列A.count(部分文字列B)
[51]:
word1 = 'hello'
word1.count('l')
[51]:
2
[52]:
'aaaaaaa'.count('aa')
[52]:
3

練習

ATGCの4種類の文字から成る文字列 str_atgc と塩基名(A, T, G, C のいずれか)を指定する文字列 str_bpname が引数として与えられたとき、str_atgc 中に含まれる塩基 str_bpname の数を返す関数 atgc_count を作成してください。

次のセルの ... のところを書き換えて atgc_count(str_atgc, str_bpname) を作成してください。

[53]:
def atgc_count(str_atgc, str_bpname):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が True になることを確認してください。

[54]:
print(atgc_count('AAGCCCCATGGTAA', 'A') == 5)
False

大文字小文字

lower, capitalize, upper メソッドを用いると、文字列の中の英文字を小文字に変換したり、大文字に変換したりすることができます。

これらの操作では、元の文字列は変化しません。

[55]:
upper_dna = 'DNA'
upper_dna.lower() # 全ての文字を小文字にする
[55]:
'dna'
[56]:
upper_dna
[56]:
'DNA'
[57]:
lower_text = 'hello world!'
lower_text.capitalize() # 先頭文字を大文字にする
[57]:
'Hello world!'
[58]:
lower_text
[58]:
'hello world!'
[59]:
lower_text.upper() #全ての文字を大文字にする
[59]:
'HELLO WORLD!'
[60]:
lower_text
[60]:
'hello world!'

文字列の比較演算

比較演算子、==, <, > などを用いて、2つの文字列を比較することもできます。

[61]:
print('abc' == 'abc')
print('ab' == 'abc')
True
False
[62]:
print('abc' != 'abc')
print('ab' != 'abc')
False
True

文字列の大小の比較は、いわゆる辞書式による比較で、文字列の最初の文字から順に比較して大小を決めます。 片方がもう片方を拡張したものであれば、拡張した方を大きいとします。

[63]:
print('abc' <= 'abc')
print('abc' < 'abc')
print('abc' < 'abd')
print('ab' < 'abc')
True
False
True
True

練習

英語の文字列 str_engsentences が引数として与えられたとき、それが全て小文字である場合、True を返し、そうでない場合、 False を返す関数 check_lower を作成してください。

次のセルの ... のところを書き換えて check_lower(str_engsentences) を作成してください。

[64]:
def check_lower(str_engsentences):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が全て True になることを確認してください。

[65]:
print(check_lower('down down down') == True)
print(check_lower('There were doors all round the hall, but they were all locked') == False)
False
False

初心者によくある誤解 — 変数文字列の混乱

初心者によくある誤解として、変数と文字列を混乱する例が見られます。たとえば、文字列を引数に取る次のような関数 func を考えます( func は引数として与えられた文字列を大文字にして返す関数です)。

[66]:
def func(str1):
    return str1.upper()

ここで変数 str2 を引数として func を呼ぶと、str2 に格納されている文字列が大文字になって返ってきます。

[67]:
str2 = 'abc'
func(str2)
[67]:
'ABC'

次のように func を呼ぶと上とは結果が異なります。次の例では変数 str2 (に格納されている文字列 abc)ではなく、文字列 'str2' を引数として func を呼び出しています。

[68]:
str2 = 'abc'
func('str2')
[68]:
'STR2'

練習

コンマ (,) を含む英語の文章からなる文字列 str_engsentences が引数として与えられたとき、str_engsentences 中の一番最初のコンマより後の文章のみかならなる文字列 str_res を返す関数 remove_clause を作成してください。ただし、 str_res の先頭は大文字のアルファベットとしてください。

次のセルの ... のところを書き換えて remove_clause(str_engsentences) を作成してください。

[69]:
def remove_clause(str_engsentences):
    ...

上のセルで解答を作成した後、以下のセルを実行し、実行結果が True になることを確認してください。

[70]:
print(remove_clause("It's being seen, but you aren't observing.") == "But you aren't observing.")
False

練習の解答

[71]:
def remove_punctuations(str_engsentences):
    str1 = str_engsentences.replace('.', '') # 指定の文字を空文字に置換
    str1 = str1.replace(',', '')
    str1 = str1.replace(':', '')
    str1 = str1.replace(';', '')
    str1 = str1.replace('!', '')
    str1 = str1.replace('?', '')
    return str1
#remove_punctuations('Quiet, uh, donations, you want me to make a donation to the coast guard youth auxiliary?')
[72]:
def atgc_bppair(str_atgc):
    str_pair = str_atgc.replace('A', 't')# 指定の文字に置換。ただし全て小文字
    str_pair = str_pair.replace('T', 'a')
    str_pair = str_pair.replace('G', 'c')
    str_pair = str_pair.replace('C', 'g')
    str_pair = str_pair.upper() # 置換済みの小文字の列を大文字に変換
    return str_pair
#atgc_bppair('AAGCCCCATGGTAA')
[73]:
def swap_colon(str1):
    #コロンの位置を取得する # findでもOK
    col_index = str1.index(':')
    #コロンの位置を基準に前半と後半の部分文字列を取得する
    str2, str3 = str1[:col_index], str1[col_index+1:]
    #部分文字列の順序を入れ替えて結合する
    str4 = str3 + ':' + str2
    return str4
#swap_colon('hello:world')
[74]:
def atgc_count(str_atgc, str_bpname):
    return str_atgc.count(str_bpname)
#atgc_count('AAGCCCCATGGTAA', 'A')
[75]:
def check_lower(str_engsentences):
    if str_engsentences == str_engsentences.lower():#元の文字列と小文字に変換した文字列を比較する
        return True
    return False
#check_lower('down down down')
#check_lower('There were doors all round the hall, but they were all locked')
[76]:
def remove_clause(str_engsentences):
    int_index = str_engsentences.find(',')
    str1 = str_engsentences[int_index+2:]
    return str1.capitalize()
#remove_clause("It's being seen, but you aren't observing.")
[ ]: