Edited at

Python文字列操作マスター

Pythonにおける基本的な文字列操作をまとめました。経験豊富な人には物足りない内容かもしれませんが...

(追記2018.12.23: print の文法をPython3対応にしました。Python2でコピペしたコードが動かない場合は from __future__ import print_function をコード辺の先頭に入れてください。)


Pythonの文字列 = immutable(変更不能)

Pythonの文字列はimmutableなので、部分的に書き換えたいというような場合でも、

新しい文字列オブジェクトとして組み立てることになります。

たとえば、文字列置換をおこなうreplaceというメソッドは置換したあとの内容をもつ別の文字列オブジェクトを返します。


連結

連結には+演算子を使います。

a = 'Python'

b = '2.7'
c = a + b
print(c) # => 'Python2.7'

順番に処理されるのでたくさん連結してもok

a = 'Python'

b = ' is '
c = 'fancy'
print(a + b + c) # => 'Python is fancy'

joinメソッドとリスト/タプルを使って連結するテクニックもありますね。

余談ですがRubyのjoinはArrayのメソッド(連結文字列を引数にとる)、Pythonのjoinはstrのメソッド(list/tupleを引数にとる)と、逆になっているのでRubyの経験が豊富な方は注意が必要です。

strings = ['dog', 'cat', 'penguin']

print(','.join(strings)) #=> 'dog,cat,penguin'


繰り返し

同じ内容を繰り返す場合は*演算子で整数を与えると指定した回数だけリピートした文字列が生成されます。

s = 'dog?'

print(s * 3) #=> 'dog?dog?dog?'


値の埋め込み

文字列に変数の値を展開する際には、3つのやり方があります。もしかしたら私が知らないだけで他にもあるかもしれません。


  1. sprintfスタイル: '%s, %s' % ('Hello', 'World')

  2. 拡張sprintfスタイル: '%(a)s, %(b)s' % dict(a='Hello', b='World')

  3. formatメソッド利用: '{0}, {1}'.format('Hello', 'World')

(注) 二つ目の正確な呼び名はよくわからないのですが、ここでは勝手に拡張sprintfスタイルと呼ぶことにしました。


sprintfスタイル

文字列に % 演算子で値またはlist/tupleを与えると以下のように展開できます。

a = 'Python'

b = 'a programming language'
print('%s is %s' % (a, b)) # => 'Python is a programming language'

c = 'World'
print('Hello, %s!' % c) # => 'Hello, World!'

文字列中の展開記号(%sなど)の個数の数だけ、与える値も必要です。多くても少なくてもいけません。展開記号が1つの場合は%のあとの値はlist/tupleにする必要はありません。(1要素のlist/tupleでも展開されます) 上の例だと最初のprint文のテンプレート文字列は展開記号%sを2つ含むので、%の後にくる値で与えるタプルの要素の数も2つになっています。テンプレート文字列中で、%という文字自体を文字として残したい場合は%%'%'2文字にするとよい です。

以下のようなフォーマット指定子があります。よくわからないうちはとりあえず%sにしておくというのも手かもしれません。フォーマット指定子の書き方はprintfのwikipediaページなどに説明を譲りたいと思います。



  • %s - 文字列として展開


  • %d - 整数として展開


  • %f - 小数点数として展開

タプルやリストを '(1, 2, 3)' のような文字列として展開してほしいときは

tuple_var = (1, 2, 3)

print('tuple_var is: %s' % (tuple_var,))

としないと3個あるのに置換先のプレースホルダが1個しかないよ!みたいに怒られてしまうので注意ですね。


拡張sprintfスタイル

※拡張sprintfスタイルというのは私が勝手につけた名前です(^^;

フォーマット文字列の%のあとに丸カッコでかこってdictオブジェクトのキーを指定し、フォーマット文字列に対して%演算子の右辺にdictオブジェクトを指定します。繰り返し同じ値を埋め込む際、すでにdict変数がある場合などに便利でしょう。

v = dict(first='Michael', family='Jackson')

print('He is %(first)s, %(first)s %(family)s.' % v)


formatメソッドの利用

formatメソッドを使うことでformatメソッド専用のテンプレート言語を使うことができます。

print('{0}, {1}'.format('Hello', 'World'))  #=> 'Hello, World'

詳しくは書式指定ミニ言語仕様を見て見てください。


置換(replace)

s = 'Today is Monday.'

ss = s.replace('Monday', 'Sunday') #=> 'Today is Sunday.'
print(ss)
s2 = 'Hello Hello'
ss2 = s2.replace('Hello', 'Bye') #=> 'Bye Bye' 第三引数を指定しなければすべて置換される
print(ss2)
s3 = 'World World'
ss3 = s3.replace('World', 'Hello', 1) #=> 'Hello World' # 第三匹数で置換する個数を指定
print(ss3)

あるパターンに従って文字列を置換する、といった処理はre(正規表現)パッケージのsubメソッドを使います。

import re

s = 'Hello World'
print(re.sub(r"[a-z]", "A", s)) #=> 'HAAAA WAAAA'


N文字目の文字の取得

s = 'abc'

n = 1 # 'a'がほしい
print(s[n-1]) # 0ベースインデックスで文字を取得

s2 = 'xyz'
print(s[-1]) # 'z' 最後の文字


部分文字列の取得(N文字目から、M文字とりだす)

s = "This is a pen."

n = 1
m = 4
print(s[n-1:n-1+m]) # 'This'
print(s[0:4]) # 'This'
print(s[-4:-1]) # 'pen'


検索

findを使います。後ろ向きに検索したいときはrfindが使えます。

findは該当文字列が見つかれば0からはじまる文字列位置を, 見つからなければ-1を返します。

s = 'abcabcabc'

index = s.find('b') # indexは1(2文字目)

第二引数で検索を開始する位置を指定できます。

s = 'abcabcabc'

index = s.find('b', 2) # indexは4(5文字目)

以下のようなコードで文字列中からすべてのターゲットを探し出せます。

s = 'abcabcabc'

target = 'b'
index = -1
while True:
index = s.find(target, index + 1)
if index == -1:
break
print('start=%d' % index)


1文字ずつ処理

string型はイテレータでもあるので、以下のようにforで処理できます。文字のlistが欲しければlist(strvalue)でいいですね。

for c in 'aiueo':

print(c)

print(list('hoge')) # => ['h', 'o', 'g', 'e']

インデックスで文字を参照しながら取り出すというやり方もあるかもしれません。

s = 'aiueo'

for i in range(len(s)):
c = s[i]
print(c)


両端の空白削除

strip, lstrip, rstripが使えます。

stripは両端からスペース・タブ文字・改行(\rおよび\n)を削除した文字列を、

lstripはstripと同等の処理を左端のみに適用したものを、

rstripはstripと同等の処理を右端のみに適用したものを返します。

s = ' x '

print('A' + s.strip() + 'B') # => 'AxB'
print('A' + s.lstrip() + 'B') # => 'Ax B'
print('A' + s.rstrip() + 'B') # => 'A xB'


改行削除(perlやrubyのchomp相当の処理)

rstripでいけそうです。ただ末尾に空白と改行がある2パターンがあり、改行の場合だけ削除したい、というような場合はrstripだけではだめそうです。

line = 'hoge\n'

msg = line.rstrip() + 'moge'
print(msg) # => 'hogemoge'

with open('./test.txt') as fh:
for line in fh:
no_line_break_line = line.rstrip()
# なにかする


全部大文字にする

upper()メソッドを使います。


print('hello'.upper())  # => 'HELLO'


全部小文字にする

lower()メソッドを使います。

print('BIG'.lower())  # => 'big'


ある文字列が部分文字列として含まれるかどうか調べる

s = 'abc'

print('b' in s) #=> True
print('x' in s) #=> False


ある文字列が部分文字列として登場する回数を数える

先ほど出てきたfindメソッドを使って自力でやってもよいですが、countという便利なメソッドがあります。

s = 'aaabbc'

print(s.count('b')) #=> 2


intを文字列に変換する

v = 1

print(str(v))
print('%d' % v)


floatを文字列に変換する

f = 1.234

print(str(f)) #=> '1.234'
print('%f' % f) #=> '1.234000'


listを文字列に変換する, tupleを文字列に変換する

変換というか、デバッグprintなどで文字列で表現したいことはありますよね。

v = [1,2,3]

print(str(v)) #=> '[1, 2, 3]'
print('%s' % v) #=> '[1, 2, 3]'

tuple1つを%sで表示しようとすると, Pythonが与えられたtupleをテンプレート用の値のリストと解釈してエラーになってしまいます。

v = (1, 2, 3)

print(str(v)) #=> '(1, 2, 3)' よい例
print('%s' % v) #=> '(1, 2, 3)'を期待するが、TypeErrorになってしまう
print('%s' % (v,)) #=> '(1, 2, 3)' よい例

joinなどを使って組み立ててみるのもいいですね。

v = [1,2,3]

print('<' + ('/'.join([ str(item) for item in v ])) + '>') #=> '<1/2/3>'

tupleオブジェクトも同様です。


dictを文字列に変換する

変換というか、デバッグprintなどで文字列で表現したいことはありますよね。

v = dict(a=1, b=2)

print(str(v)) #=> "{'a': 1, 'b': 2}"
print('%s' % v) #=> "{'a': 1, 'b': 2}"

keysやリスト内包表記、joinを使ってワンライナーで文字列生成してもいいですね。

v = dict(a=1, b=2)

print('<' + ', '.join([ '%s=%s' % (k, v[k]) for k in v.keys() ]) + '>') #=> '<a=1, b=2>'


(2系) バイト列をunicode文字列にする

ファイルやソケットから読み込んだデータはそのままだとバイト列なので、unicode文字列に解釈してあげないと文字単位の操作がうまくできません。Python2系(2.7など)ではstr(バイト列)とunicode(文字列)は区別されており、Webアプリケーショなど入力にマルチバイト文字が想定されるシーンでは文字列はunicodeオブジェクトとして扱ったほうがよいでしょう。バイト列をエンコーディングを指定してunicode文字列として解釈するためにはdecodeメソッドを使います。

with open('utf8_content_file.txt', 'rb') as fh:

byte_content = fh.read() # ぜんぶ読み込む, この時点ではバイト列
print len(byte_content) # バイト数
unicode_string = byte_content.decode('utf-8') # utf-8エンコーディングで、文字の並びとして解釈
print len(unicode_string) # 文字数


(2系) unicode文字列をバイト列にする

逆にファイルやソケットに書き込むとき、文字列をバイト列にしないといけないです。そういうときはunicodeオブジェクトのencodeメソッドをつかいます。

unicode_string = u'マルチバイト文字の文字列'

with open('./utf8_content_file.txt', 'wb') as fh: # 書き込みモードでopen
byte_content = unicode_string.encode('utf-8') # utf-8エンコーディングで表現した場合のバイト列を取得
fh.write(byte_content) # バイト列を書き込み


テンプレートエンジンを使う

テンプレートエンジンは機能がとても豊富なのでここではいくつかのメジャーなライブラリの紹介にとどめます。

jinja2が一番メジャーでしょうか.


参考リンク