.. title:: Pythonで心理実験 - 例題3-4 例題3-4:文字列の料理法 ========================== **A:** さて。ひたすらデータ型と演算子の話をしてきたが、今回で一区切りだ。今回は文字列について説明する。 まず、pythonの文字列には大きく分けてstr型とunicode型がある。日本語を使いたい場合はunicodeを使わないといけない事、unicode文字列を生成するにはuを つける事は前に話した通りだ。この例では変数aとbに"test"という文字列を格納しているが、aはstr型、bはunicode型だ。 :: >>> a = "test" >>> b = u"test" >>> type(a) >>> type(b) **B:** うっ。いきなりややこしい。 **A:** 文字列、というか文字コードは昔からずーっとややこしい問題だらけなんだよな。 そこら辺の話題に踏み込んでいくと泥沼にはまるから、心理実験に必要な最小限の事を押さえるだけにしよう。 正直なところ、日本語の文字列を生成する時にはuをつけるとだけ覚えておけば困る事はほとんどない。str型でもunicode型でも同じように操作する事が出来る。 .. csv-table:: :delim: $ a[x]$文字列aのx番目の文字を取り出す。 a[x:y]$文字列aのxからy番目の文字を取り出した部分文字列を生成する。 a[x:y:s]$文字列aのxからy番目の文字をs文字毎に取り出した部分文字列を生成する。 a + b (aとbは文字列)$aとbを連結した文字列を生成する。 x in a (xとaは文字列)$文字列xが文字列aに含まれていればTrue、なければFalse。 a == b (aとbは文字列)$文字列aとbが等しければTrue、そうでなければFalse。 a < b など(aとbは文字列)$文字列aとbの文字を先頭から辞書順に比較。 **A:** まあこういうのは実際に試してみるのが一番いい。 :: >>> a = "test" >>> b = u"test" >>> type(a) >>> type(b) >>> a == b True >>> a + b u'testtest' >>> 'es' in a True >>> 'se' in a False **B:** あれ、str型の'test'とunicode型の'test'は同じ文字列と判定されるんですね。 **A:** まあ違うと判定された方が便利な時と不便な時があるが、直感的ではあるだろ。 **B:** str型とunicode型の文字列の連結も出来る。結果はunicode型になるんですね。 **A:** この例ではたまたま連結後の文字列もstr型で表現できるものだが、日本語などが含まれているとunicode型じゃないと表現できないからね。 **B:** ふむふむ。 **A:** リストと同じように、文字列にもたくさんのメソッドがある。全部説明すると大変なので、独断と偏見で便利そうな物をいくつか紹介しておこう。 .. csv-table:: :delim: $ a.strip()$文字列aの先頭と末尾の空白文字を取り除く。 a.split(sep) (sepは文字列)$文字列aをsepに含まれる文字で分割したリストを生成する。 a.join(s) (sは文字列を要素とするリスト)$リストsに含まれる文字列をaを区切り文字として連結する。 a.zfill(n) (nは自然数)$文字列aの先頭に0を挿入してn文字の文字列を生成する。 a.center(n) |HTMLBR| a.ljust(n) |HTMLBR| a.rjust(n)$文字列aの先頭または末尾に空白文字を挿入してn文字の文字列にする(nは自然数)。空白文字以外で埋めたい場合はnの後に埋めるための文字を指定する。 a.lower() |HTMLBR| a.upper()$文字列aに含まれるアルファベットを小文字または大文字に揃える。 a.isalnum() |HTMLBR| a.isalpha() |HTMLBR| a.isdigit() |HTMLBR| isspace()$文字列aに含まれる文字がアルファベットか数字、アルファベットのみ、数字のみ、空白文字のみであればTrue、そうでなければFalseを返す。 a.count(s,m,n)$文字列aのm文字めからn文字めまでの間に文字列sが現れる回数を返す。m,nが省略された場合は文字列a全体を探す。 a.find(s,m,n)$文字列aのm文字めからn文字めまでの間に文字列sが含まれる場合、その最初のもののインデックスを返す。m,nが省略された場合は文字列a全体を探す。 a.replace(s,t)$文字列aのに含まれる文字列sのすべてを文字列tに置き換えた文字列を返す。tの後に自然数nが指定された場合は、最初のn個までの文字列sをtに置き換える。 **B:** むむむ。わかるような、わからないような…。 **A:** 正直なところ、実際に必要に迫られないとこれらの機能が何に使えるのかなんてピント来ないと思う。 この中でsplit()やzfill()、strip()は私自身が実験プログラムを書く時に時々使っているから、そのうち目にする事があるだろう。例えばsplit()はカンマ区切りや/区切りで並べられたデータを人から渡された時に、それをひとつひとつのデータにばらすのに使える。 :: >>> data = '2009/2/12' >>> data.split('/') ['2009','2','12'] **A:** zfill()は被験者に番号を割り振って、その番号のファイル名にデータを保存する時なんかのファイル名生成に便利だ。 :: >>> subjectNumber = 17 >>> 'FILE' + str(subjectNumber).zfill(4) + '.txt' 'FILE0017,txt' **A:** こういう風に0で埋めてやらないと、被験者5番のと17番のファイルがdata5.txtとdata17.txtになってファイル名の順番に並べた時にdata17.txtがdata5.txtより前に来ちゃうからね。 **B:** あのー。str()って何ですか? **A:** ああ、説明するの忘れてた。subjectNumberは整数であってstr型の変数じゃないだろう? そのままではzfill()などの文字列を操作する関数を使う事が出来ないから、str型に変換しないといけない。 その変換をおこなう関数がstr()だ。他には整数に変換するint()や浮動小数点数に変換するfloat()がある。 .. csv-table:: :delim: $ str(a)$aをstr型(文字列)に変換する。 int(a)$aをint型(整数)に変換する。 float(a)$aをfloat型(浮動小数点数)に変換する。 **B:** ふむふむ。…あれ、ところでこのfind()っていうのはもしかして、リーディングスパンテストのターゲット語の出現位置を数えた時(注:例題1-5)のような場合に使えるんですかね? **A:** そう。実はあの時もfindを使って数えたのさ。 :: >>> a = u"それは、ゆれながら水銀のように光って上に上がった" >>> a.find(u"水銀") 9 **A:** 文字列aの長さはリストと同じようにlen(a)で求められるから、課題文がkadai、ターゲット語がtargetというリストに順番が対応するように保存されているのであれば、こうすれば例題1-5のsentenceListを作成できる。 :: sentenceList = [] for i in len(kadai): index = kadai[i].find(target[i]) targetLength = len(target[i]) sentenceList.append([kadai[i], index, targetLength]) **B:** がーん。ひとつひとつ数えたぼくの苦労は一体…。 **A:** そういう苦い経験がプログラミングを覚えようという意欲になるのさ。耐えよ。若者。 **B:** しくしく。 **A:** さて、最後に文字列に対する%演算子の説明をしておこう。こいつはデータを文字列に変換したい時に欠かせない演算子だ。 .. csv-table:: :delim: $ format % values$formatに従ってvaluesを文字列に変換する。 **B:** ??? **A:** %演算子の機能は非常に複雑なので、詳しくはちゃんとしたpythonの教科書を見てほしい。 ここで紹介しておきたいのは、valuesにタプルを指定する例だ。 :: >>> "%d,%d,%s,%.2f" % (12,0,'s',0.67894323) '12,0,s,0.68' **B:** ??? さっぱりわかりません。 **A:** タプルというのは例題1-5でちらっと出てきた「リストと似て非なるもの」だ。あの時、 **リストでは駄目でタプルじゃないと出来ない処理があるといったけど、この%演算子による変換がそのひとつだ** 。 この例では、%演算子の右側に12, 0, 's', 0.67894323という4つの値を並べたタプルが置かれている。これらの値を%演算子の右側にある文字列へ埋め込んでいくのが%演算子の役割だ。 **B:** ちょっと待って下さい。%演算子って、%がいくつも出てきてるのでどれのことか分かりません。 **A:** あー。確かに初めての人には分かりにくいな。これが%演算子だ。 :: >>> "%d,%d,%s,%.2f" % (12,0,'s',0.67894323) ↑これ **B:** "と(の間のやつですね。 **A:** さて、%演算子の前にあるのがformat文字列、つまり埋め込み先の文字列だが、ここで重要なのがこれまた%だ。 %演算子はformat文字列中に%を見つけると、%に続く文字に従ってタプル中の値を変換して文字列を組み立てていく。%に続く文字は以下に示す **変換型** でなければいけない。 .. csv-table:: :delim: $ 変換型$意味 dまたはi$符号付き10進数 o$符号なし8進数 u$符号なし10進数 x$符号なし16進数(小文字) X$符号なし16進数(大文字) e$指数表記の浮動小数点数(小文字) E$指数表記の浮動小数点数(大文字) fまたはF$10 進浮動小数点数 gまたはG$浮動小数点数(指数部が-4以上または制度以下の場合には指数表記、それ以外は10進表記) c$文字1文字 r$文字列(repr()) s$文字列(str()) %$半角のパーセント記号(%) **A:** 本来ならばさらに **フラグ** とものをつけて数値を左詰めにしたり0や空白文字で出力桁を揃えたりできるんだが、心理実験でそこまで必要になる事は少ないと思うので必要に応じて各自で勉強してほしい。 **B:** うーん。だめだ。理解しようという気力が湧いてきません。 **A:** ん。まあこんな表をいきなり見せられてもやる気出ないよな。まあ例をじっくりと見ていくか。format文字列とタプルの要素はこのように出てきた順番に一対一に対応する。 .. figure:: img/03-4-01.png **A:** 1番目の%はd、つまり整数として出力せよという指定になっている。一方タプルの1番目の要素は12。だからそのまま'12'という文字列になる。2番目の%も同様だね。 3番目の%はsと指定されているが、タプルの3番目の要素も文字列の's'なので、そのまま's'として出力される。4番目の%がちょっと曲者だが、fは10進数の浮動小数点という指定、%とfの間の.2は小数点2桁まで出力しなさいというフラグだ。 というわけで、0.67894323の小数点3桁が四捨五入されて'0.68'という文字列に変換されている。 **B:** むむむ~。 **A:** 実際の心理実験で使いそうな例を挙げたんだが、「埋め込む」という感じがかえって分かりにくかったかも知れないな。 例えば面接の通知など、宛先や時間など文章の一部分だけが違う手紙をたくさん作らないといけないとする。このような場合、以下のようなformat文字列とリストを作って%演算子を使って埋め込んでいくといい。 :: data = ((u'○山×夫',u'2月15日',u'第1面接室'), (u'凸川凹美',u'2月16日',u'第3面接室'), # 中略 ) format = u'%s様 以下の日程で面接を行いますので...(中略)... 日時:%s 会場:%s' for d in data: letter = format % d # 以下、letterに格納された文字列を印刷するなり何なりと **B:** あー。これは便利そうな。 **A:** 実験データの場合、ただデータをカンマやタブで区切って出力するだけの事が多いから、%dやら%fやらをひたすら','や'\\t'で区切ったformat文字列を使う場合が多いけど、こういう例だと「文書にデータを埋め込む」という実感があるんじゃないかな。 **B:** ? \\tってなんですか? **A:** ああ、これはformat文字列の中にタブを出力したい時に、タブをそのまま入力しても無視されるのでこういう風に書くのさ。他に改行文字は\\nと書く。ちなみに\\そのものを文字列に入れたい場合は\\\\と書く。 この辺りの事はいずれきちんと説明したいんだけど、なかなか時間がないだろうなあ。 **B:** ふーん。あと、%sと%rの説明にあるstr()とかrepr()ってなんですか? **A:** これは文字列を生成する時にpythonが用いる関数を示している。まあ通常は%sを使っておけばいい。%rを使わないといけないような局面は 心理実験の場合ちょっと思いつかないな。 **B:** そんなら載せなきゃいいのに… **A:** ん? 何か言ったか? **B:** い、いや別に。 **A:** まあ、何にしてもこれで演算子の話もようやく一段落かな。これからは説明した演算子をどんどん使っていくので、わからなくなったらここへ戻ってきて復習するように。 **B:** へーい。 .. |htmlbr| raw:: html