5. 文字と情報

人類の扱う情報の大半は、文字によって伝えられます。だから、文字や文字列を扱うことは、情報処理の基礎中の基礎となります。

ひとつ嬉しい話をすると、文字列処理はPythonの得意分野です。 つまり、とてもプログラミングしやすいです。 しっかり練習して、文字列処理をマスターしましょう。

5.1. 文字

文字は、コンピュータの内部では、文字コードと呼ばれる番号で表現されています。

ascii-fs8.png

文字コードは覚える必要はありません。Python の関数で相互に変換することができます。

文字コードcから文字へ: chr(c)

[1]:
chr(65)
[1]:
'A'

文字’A’から文字コードへ: ord('A')

[1]:
ord('A')
[1]:
65

Let’s try

の文字コードを調べてみよう!

Pythonの文字

Pythonでは、文字は1文字長の文字列に過ぎません。ユニコードに対応しているため、日本語も1文字として扱うことができます。

文字 c の種類の判定は文字コードに基づいておこないます。 Python は便利な文字列メソッドも用意してくれているので、通常はコメントの方を使います。

数字の判定

'0' <= c and c <= '9'   # もしくは、c.isdigit()

英大文字の判定

'A' <= c and c <= 'Z' # もしくは、c.isupper()

英小文字の判定

'a' <= c and c <= 'z' # もしくは c.islower()

ひらがなの判定

'ぁ' <= c and c <= 'ん'

カタカナの判定

'ァ' <= c and c <= 'ン'

日本語処理(自然言語処理)

競技プログラミングは、国際大会をベースにしているので、英数字の処理が中心です。 しかし、日本人としては日本語を処理する機会も多くあります。 より高度な日本語処理(自然言語処理)は、3年生で学ぶ予定になっています。

5.2. 文字列

文字列は、文字通り、文字の列です。 リストと同じように、文字列を先頭から数えて i 番目の文字のように取り出すことができます。

例. 文字列の最初の文字

[2]:
s = "ABC"
s[0]   # 最初の1文字
[2]:
'A'

Python は、文字と文字列を区別しません。文字は長さ 1 の文字列となります。"A"'A'もどちらも文字列リテラルです。文字列sの長さは、リストと同様にlen(s)で得られます。

[ ]:
s = 'Hello, World'
len(s)

特殊な文字は、エスケープシーケンスで表現できます。

エスケープシーケンス

意味

\\

バックスラッシュ

\'

一重引用符

\"

二重引用符

\a

ASCII 端末ベル

\b

ASCII バックスペース

\f

ASCII フォームフィード

\n

ASCII 行送り

\r

ASCII 復帰

\t

ASCII 水平タブ

\v

ASCII 垂直タブ

\xhh

16進数hhを持つASCII文字

\Uxxxxxxxx

32ビットのUnicode文字

[ ]:
s = 'Hello\nWorld\n'
len(s)

複数行にわたる文字列(テキスト)は、トリプルクオート(''', """)で囲むことでも記述できます。

[ ]:
s = """
吾輩は猫である
名前はまだない
"""
len(s)

5.2.1. 文字列と文字リスト

Pythonのデータ構造は、オブジェクト指向プログラミングの設計手法にしたがって、操作がよく共通化されています。例えば、列の特徴があるデータ構造は、全てlen()で列の長さが取れ、インデックスで\(n\)番目の値が得られるようになっています。

[ ]:
s = "abc"
print(len(s))
print('s[0]', s[0])
print('s[-1]', s[-1])


[ ]:
a = ['a', 'b', 'c']
print('len(a)', len(a))
print('a[0]', a[0])
print('a[-1]', a[-1])

文字列と「文字のリスト」の大きな違いは、破壊的な操作、つまり変更できるかどうかにあります。 リストは変更できますが、文字列は不変で変更できません。タプルと同じです。

不変性(Immutablitiy)

値が変更できないこと。 不変性が保証されると、グループで開発するときなど、値を勝手に変更されず、 プログラムの一貫性を保ちやすい(つまりバグを防げる)

[21]:
s = "abc"
s[0] = "A" # 先頭を変更 (ただし、エラーになります)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-21-faf39c17bf80> in <module>
      1 s = "abc"
----> 2 s[0] = "A" # 先頭を変更 (ただし、エラーになります)

TypeError: 'str' object does not support item assignment
[22]:
a = ['a', 'b', 'c']
a[0] = 'A'  # こちらは変更可能
print(a)
['A', 'b', 'c']

文字列の内容を変更したいとき

文字列を文字リストに変換して操作することができます。

  • 文字列から文字リストに変換する: cs = list(s)

  • 文字リストを変更する

  • 文字リストから文字列に戻す s = "".join(cs)

例題(ひらがなとカタカナ)

文字列中のひらがなをカタカナに変換してみよう

入力例:

めじろはなこ

出力例:

メジロハナコ

文字列を文字のリストに変換し、ひらがなの場合は文字コードを96大きくします。 そして、再度、文字列に変換して戻します。

[3]:
s = "めじろはなこ"
cs = list(s)  # 文字のリストに変換
for i, c in enumerate(cs):
    if 'ぁ' <= c and c <= 'ん':
        cs[i] = chr(ord(c) + 96)
s = ''.join(cs) # 文字列に変換
print(s)
メジロハナコ

5.3. 文字列操作

文字列は、メソッドを使って文字列操作することができます。 Pythonは、便利なメソッドが揃っているので是非使えるように練習しておきましょう。

基本操作

len(s)

文字列sの文字数

s[i]

文字列si番目の文字

s[i:j]

i 番目から j 番目までの部分文字列

s[i:]

i 番目から末尾までの部分文字列

s[i:j]

先頭からj番目までの部分文字列

x in s

文字列sにxが含まれるかどうか

s.find(x)

文字列s内の最初のxの位置(ない場合は-1)

s.rfind(x)

文字列s内の最後のxの位置(ない場合は-1)

s.count(x)

文字列s内のxの出現回数

新しい文字列

""

空の文字数

str(e)

eの文字列表記

f"{e}"

書式文字列中に式eを埋め込んだ文字列

s.join(a)

文字列リストaをsで連結した文字列

文字列の判定

s.startswith(x)

文字列sがxで始まるかどうか

s.endswith(x)

文字列sがxで終わるかどうか

s.isupper()

文字列sが英大文字かどうか

s.islower()

文字列sが英小文字かどうか

s.isdigit()

文字列sが数字かどうか

文字列の変形

s + t

文字列sをtを連結した文字列

s * n

文字列sをn回連結した文字列

s[::-1]

文字列sを反転した文字列

s.replace(o, n)

文字列 s 内の o を n に置き換えた文字列

s.upper()

文字列 s を英大文字に変換した文字列

s.lower()

文字列 s を英小文字に変換した文字列

list(s)

文字列 s の文字リスト

s.split(c)

文字列 s を c で分割した文字列のリスト

文字列ライブラリ

Python には、さまざまな文字列ライブラリがあります。

例題(文字列操作)

次を文字列操作によって求める

  1. 'Hello'を全て英大文字に変換する

  2. 'Hello'の中に含まれるlの数

  3. -123の先頭の-を取り除いた部分文字列

  4. '*'が28個続く文字列

  5. '12', '50', '00'':'で連結する

  6. 1,3,4,8をカンマ区切りから空白区切り(1 3 4 8)に変換する

  7. 1,3,4,8から、数列([1,3,4,8])に変換する

[12]:
s = 'Hello'
s.upper()
[12]:
'HELLO'
[13]:
s = "Hello"
s.count('l')
[13]:
2
[14]:
s = "-123"
s[1:]
[14]:
'123'
[15]:
s = '*'
s * 28
[15]:
'****************************'
[16]:
a = ['12', '50', '00']
':'.join(a)
[16]:
'12:50:00'
[17]:
s = '1,3,4,8'
s.replace(',', ' ')
[17]:
'1 3 4 8'
[18]:
s = '1,3,4,8'
s.split(',')  # このままでは文字のリスト
[18]:
['1', '3', '4', '8']
[20]:
s = '1,3,4,8'
[int(c) for c in s.split(',')]
[20]:
[1, 3, 4, 8]

例題(数字の和)

図のようなリング状の文字列 s の任意の位置から、時計回りに連続した文字をいくつか選んで、 文字列 p が作れるかを判定するプログラムを作成してください。

リング

入力例

vanceknowledgetoad
advance

出力例

True

入力例

vanceknowledgetoad
advanced

出力例

False

出典 AOJ ITP1_8_D Ring

このプログラムは、文字列処理が苦手なC言語で解こうとすると、かなり難問です。 ただし、Pythonなら少し工夫すれば、基本文字列操作で解くことができます。

[ ]:
s = "vanceknowledgetoad"
p = "advance"
p in s + s

どうして文字列sを連結しているの?

と、質問されることがありますが、連結するとどうなるか落ち着いて考えてみてください。

例題(数字の和)

与えられた数の各桁の和を計算するプログラムを作成して下さい。

入力例

1234

出力例

10

出典 AOJ ITP1_8_B Sum of Numbers

まず、基本通り、確実にプログラミングできるようになりましょう。

[ ]:
s = "1234"
n = 0
for i in range(len(s)): # 文字列の大きさだけ繰り返す
    n += int(s[i])
print(n)
[10]:
s = "1234"
n = 0
for c in list(s):  # 文字のリストとして1文字ずつ処理する
    n += int(c)
print(n)
10

リスト内包記法を使えば、コンパクトに書くことができるようになります。 (書ける必要はありまえんが、ソースコードを読んで理解するためには読めるようになる必要があります。)

[11]:
s = "1234"
sum(int(c) for c in list(s))
[11]:
10

リスト内包記法の[]を省略

リスト内包記法を引数で用いるときは、[ ]を省略してもよいです。

sum([int(c) for c in list(s)])

5.3.1. 辞書を使ってみる

例題(文字の数)

文字列sに含まれる、各アルファベットの数を数えるプログラムを作成して下さい。 なお、英小文字と英大文字は区別しません。

入力例

This is a pen.

出力例

a : 1
e : 1
h : 1
i : 2
n : 1
p : 1
s : 2
t : 1

出典 AOJ ITP1_8_C Counting Characters

ヒント

  • 文字列内のアルファベットを 文字コード c に変換し、count[c] += 1する

  • 文字列内の文字をすべて、辞書で数え、アルファベットのみ出力する

辞書(dict)は、文字列をキーして値を管理できるデータ構造です。

まずは、辞書って便利だなと感じてください。

[9]:
# 入力
s = "This is a pen."

d = {} #空の辞書を作る

for c in list(s.lower()):
    d[c] = d.get(c, 0) + 1

print(d)

for key in sorted(d.keys()):
    if key.isalpha():
        print(key, ':', d[key])

{'t': 1, 'h': 1, 'i': 2, 's': 2, ' ': 3, 'a': 1, 'p': 1, 'e': 1, 'n': 1, '.': 1}
a : 1
e : 1
h : 1
i : 2
n : 1
p : 1
s : 2
t : 1

辞書は、リストのインデックス(数値)の代わりに文字列が使えるようになります。 文字列を学んだら、あわせてマスターしておきたいデータ構造です。

主な辞書(d)の操作

コード

説明

d = {}

空の辞書を生成し、d という名前にする

d[key]

key に対する値をえる (key がない場合はエラー)

d.get(key,v)

key に対する値をえる (key がない場合は v を使う)| |key in d| 辞書内dkeyが存在するかどうか?

d[key]=v

key に対応する値vをセットする

len(d)

辞書 d の大きさ(キーの数) をえる

d.keys()

辞書 d のキーのリストをえる

辞書については、「アルゴリズムとデータ構造編」に進んだあと、 辞書の作り方を含め、もう一度、解説します。

5.4. 演習問題

課題リストより、簡単な文字列問題を集めてみました。