] 章テキスト をやるのに translate を効果的にコールしようと思ったら、ちょっと事前に準備が必要だ。 translate の第 1 引 数は変換テープルである。こでは変換をやりたいわけではないので、「変換なし」というのを指定した第 1 引数を用意する。さて、第 2 引数は削除したいキャラクタ群である。タスクでは「キープする」 ( つまり削除 しない ) キャラクタセットが与えられるとあるので、第 2 引数としてこのセットの「補集合」一一一保存しない 全キャラクタによる集合・一 - を用意しなければならない。こうした事前準備を一度に行うにはクロージャが ベストだ。これにより、必要に応じて仕立て上げた高速なフィルタリング関数を得るのである。 import string # 全キャラクタによる再利用可能な文字列を作る。 # これには「まったく変換しない」変換テープレの役目も担わせる。 = string ・ maketrans( ' allchars def makefilter(keep) : 文字列を取り、 keep にあるキャラクタのみによる部分コピーを返すような 関数を返す。 keep はプレーン文字列でなけれはならないのに注意。 # keep にないキャラクタのみから成る文字列を作る。これは keep の # 補集合で、つまり削除しなければならないキャラクタ群。 = allchars. translate(allchars, keep) delchars # 目的のフィルタリング関数を作って ( クロージャとして ) 返す def thefilter(s) : return s. translate(allchars, delchars) return thefilter if name maln just vowels = makefilter('aeiouy' ) print just vowels('four score and seven years ago' ) # 出力 : ouoeaeeyeaao print just vowels('tiger, tiger burning bright' ) # 出力 : ieieuii 考察 このレシピを理解する鍵は、塚 thon 標準ライプラリ st ⅱ ng モジュールの maket て ans 関数および文字列オプ ジェクトの translate メソッドの定義の中にある。文字列に translate をコールすると、その文字列のコピー を返すが、この各キャラクタは第 1 引数で渡した変換テープルの中身で置換してあり、また第 2 引数で指定 したキャラクタは削除してある。 maket て ans は変換テープルを生成するユーティリティ関数である。 ( 変換テー プルは厳密に 256 キャラクタから成る文字列である。 translate メソッドの第 1 引数として変換テープル t を 渡すと、メソッドのコール対象となった文字列のキャラクタ ( は、結果文字列中ではキャラクタ t [ ord ( c ) ] に 変換される ) 。 こではフィルタリングを準備と実行の 2 フェーズに分割することで効率を最大化している。全キャラク タから成る文字列が再利用可能なことは明らかなので、これをモジュールの import 時に一度だけ構築してグ ローバル変数とする。これによりすべてのフィルタリング関数は単一の「全キャラクタ文字列オプジェクト」 を使用することになり、メモリをまったく無駄にしない。削除するキャラクタによる文字列が translate メ
] 章テキスト では 256 種類までのキャラクタしか使えない。一方 U ⅲ code には数万ものキャラクタがあり得るので、 Unicode キャラクタは 2 バイト以上を占める。だからキャラクタとバイトに区別を付ける必要がある。 thon 標準の文字列はバイト文字列であり、 Python の「キャラクタ」は長さが 1 の文字列、つまり単一の バイトだ。 thon 標準の文字列型のインスタンスをあらわす用語としては、他に「 & bit 文字列」や「プレー ン文字列」といったものがある。このレシピでは、これらすべてをそのバイト指向性に注目し、バイト文字 列と呼ぶ。 python の Unicode キャラクタは、その 10 鷓 integer 同様、あらゆるキャラクタを保持するに足る巨大な抽 象オプジェクトである。その内部表現を気にする必要はない。 Unicode キャラクタの内部表現が問題になるの は、これをバイト指向の関数、たとえば file の write メソッドや、ネットワークソケットの send メソッドに 送ろうとする場合のみで、そうでなければキャラクタのバイト表現を決める必要はないからである。このよ うに Unicode をバイト文字列に変換することを、文字列の「エンコード」と呼ぶ。逆に、 Unicode 文字列を ファイル、ソケットその他のバイト指向オプジェクトからロードするときは、読み込んだ文字列をバイトか らキャラクタに「デコード」しなければならない。 Unicode オプジェクトをバイト文字列に変換するにはさまざまな方法があり、このそれぞれを「エンコー ディング」と呼ぶ。歴史的 / 政治的 / 技術的理由により、「正しい」工ンコーディングというものは存在しな い。すべてのエンコーディングは大文字小文字の関係ない名前を持ち、この名前が encode および de ( ode メソッ ドのパラメータとして渡される。以下は知っておきたい少数のエンコーディングである : ・ UTF -8 工ンコーディングは、すべての Unicode キャラクタを扱える。これはまた ASC Ⅱに対する後方 互換性がある、つまり、純粋な ASCII ファイルは UTF-8 ファイルでもあると考えることができ、また ASCII キャラクタしか使っていない UTF-8 ファイルは、同じ内容の ASCII ファイルと区別がつかな い。 UTF-8 が、特に古い Un ⅸツールにおいて高い後方互換性をもつのは、この特性のためである。 UTF -8 は Un ⅸで圧倒的に優勢であり、また XML 文書のデフォルトエンコーディングでもある。 UTF- 8 の大きな弱点は、東洋言語のテキストにあまり効率的でないことである。 ・ UTF -16 工ンコーディングは Microso れの OS と Java 環境で好まれている。西洋言語に対してはそれ ほどでもないが、東洋言語には効率的だ。 UTF -16 の変種は UC & 2 と呼ばれることもある。 ・ ISO -8859 シリーズのエンコーディング群は、すべて ASC Ⅱのスーパーセットであり、それぞれが 256 の異なるキャラクタを扱える。このエンコーディング群では、 Unicode キャラクタのすべてをサポー トすることはできす、特定の言語、あるいは言語ファミリーをサポートするのみである。レⅱ n -1 とも 呼ばれる ISO -8859-1 は、ほとんどの西欧およびアフリカ言語をカバーするが、アラヒ、ア語は含まない。 レⅱ n -2 とも呼ばれる ISO -8859-2 は、ハンガリー語やポーランド語など東欧言語の多くをカバーする。 ISO -8859-15 は昨今のヨーロッパで非常にポピュラーだが、これは基本的に ISO -8859-1 にユーロの通 貨記号キャラクタを加えたものである。 Unicode キャラクタのすべてをエンコードしたいなら、 UTF -8 を使いたいと思うはずだ。他のエンコーディ ングと付き合わねばならないのは、他のアプリケーションや入力デバイスが生成したデータが渡される場合 や、これとは逆に、下流のアプリケーションや出力デバイス向けに、データを特定のエンコーディングで用
]. IO 文字列をキャラクタセットでフィルタリングー 23 ソッドの第 2 引数に必要だが、これはキープするキャラクタの集合に依存したものとなる。つまり、その補 集合として定義してやらねばならない。これは translate がキープしたくないキャラクタを指定する構文に なっているからだ。というわけで、ファクトリ関数 makefilter の中で「削除指定キャラクタ」文字列を構築 する。この構築は「キープするキャラクタ」を全キャラクタ文字列から translate メソッドで削除することに よって素早く実行される。 translate メソッドは非常に高速なので、結果として出てくる小さくて有用な関数 の構築と動作もそうなる。レシピをメインスクリプトとして走らせた際に実行されるテストコードは、 makefilter のコールによりフィルタリング関数を構築し、これを ( makefilte てのコールを名前に単に代入して ) 名前にバインドし、さらにフィルタリング関数を文字列に対してコールして結果をプリントする方法を示し ている。 ちなみに、 allchars を引数としてフィルタリング関数をコールした場合、キャラクタセットはアルファベッ ト順で重複のない、文字列の基準的な形 (canonicstringform) にされる。このアイディアを使えば、任意の文 字列で与えられたキャラクタセットを基準的な形にして返す、非常にシンプルな関数が書ける。 def canonicform(s) : ・文字列 s を与えられると s を基準的な形 ( アルファベット順で 重複なし ) にした文字列を返す。 return makefilter(s)(allchars) 「解法」では、戻り値になる入れ子関数 ( クロージャ ) を作るのに def 文を使っている。 def は関数を作る、最 も普通で一般的で明白な方法だからである。しかしお好みであれば def の代わりに lambda を使い、 makefilter 関数の return 文を 1 行だけの return lambda 文に変えることができる。 return lambda s: s. translate(allchars, delchars) パイソニスタの大部分は lambda より def を使った方が明白で読みやすいと考えているが、全員ではない のだ。 このレシピでは文字列をキャラクタのセット ( 集合 ) として見ているので、 sets. Set 型 ( thon2.4 では新ビ ルトイン型 set) を使うこともできる。 translate メソッドのパワーとスピードのおかげで、この種のタスクで は set を使うより文字列を直接操作した方が高速なことが多い。とはいえ「レシピ 1.8 文字列にキャラクタセッ トの文字が含まれるか調べる」でも触れた通り、ここで示した関数は Unicode 文字列には使えず、通常の文 字列にのみ作用する。 Unicode 文字列では、かなり違った準備を行う必要がある。 Unicode 文字列の translate メソッドは引数を 1 つだけ取るが、これは文字列中の各キャラクタのコード番号をインデックスとしたマッピングまたはシーケ ンスである。マッピングのキー ( またはシーケンスのインデックス ) にコードのないキャラクタは、出力文字 列にそのままコピーされる。それ以外の場合、各キャラクタコードに対応した値は Unicode 文字列 ( そのキャ ラクタを置き換えるもの ) または None ( キャラクタを削除する場合 ) でなければならない。非常に優れたパワフ ルなアレンジではあるが、不幸なことにプレーン文字列でのやり方と違っているので、我々は書き直さねば ならない。 普通、 Unicode 文字列のキャラクタ置換や削除を行う場合、 translate メソッドの引数に di ( t か list を使う。
1 . O イントロダクション 5 もう 1 つ、トリプルクオート ( 円「 " 」どちらの引用符を使ってもよいが、開始と終了で対応させること ) で 文字列のキャラクタにループも掛けられる : スライスは拡張できる。 3 番目のパラメータは歩幅、つまりスライスの刻みである : spans three lines. bigger string that ThiS iS an even bigger = 囲むという手もある : 各キャラクタにはインデクシングでアクセスできる : 常に新しい文字列オプジェクトを生成する、ということだ。また、文字列はキャラクタのシーケンスなので、 文字列は変更不能であるが、これはつまり、文字列に対する操作は元々の文字列を変化させるのではなく、 he110 = He110 \ u0020W0r1 になる : 釈されす、そのまま残される。最後になるが、文字列リテラルの前に u または U を付けると、 Unicode 文字列 raw 文字列では、バックスラッシュ ( 日本のキャラクタセットでは円記号 ) によるエスケープシーケンスは解 with a backslash and a newline in it" big = r"This is a long string\ "raw" ( 生 ) 文字列とすることもできる : オプジェクトの中でニューラインキャラクタとして保存される。文字列リテラルの前に r か R を付け、これを トリプルクオートを使えば、継続キャラクタは不要である。文字列リテラル中の改行は、 thon の文字列 スライスで文字列の一部にアクセスすることもできる : mystr[-2] mystr[0] mystr = "my string' mystr[1:4] mystr[3 : ] mystr[-3:] mystr[1::2] mystr[:3:-1] # ' string ・ # 'gnirt'
398 ー IO 章システム管理 鎖による英語のシミュレーションと呼ぶと、 import random, string class password(object) : ちょいと立派だ # 英単語の入った大きなファイルであれはなんでもよい。バスティシュの # 材料のテキストである self. data が巨大な文字列になればよい = open("/usr/share/dict/words"). read( ). lower( ) data def renew(self, n, maxmem=3) : ー self. chars に n 個のランダムなキャラクタを積む。 末尾「ヒストリ」は最大で maxmem キャラクタとする。 self. chars = for i in range(n) : # self. data をランダムに " ローテーション " する randspot = random. randrange(len(self. data)) = self. data[randspot: ] + self. data[ :randspot] self. data # 以下は n-gram の取得 where = # まず self. chars から末尾 maxmem 個のキャラクタを取る # i ← maxmem の場合は末尾 i 個のキャラクタ、つまり # self. chars の全キャラクタを取ることになるが、これでよい # スライシングはこれに耐えるし、アルゴリズムにもなじむ . join(self. chars[-maxmem: ]) locate = while where く 0 and locate: # data の中で locate の並びの n-gram になる部分を探す = self. data. find(locate) where # data 中に locate の並びの n-gram が見つからないときは # where=-l となり while ループがもう一度回る # このときのために n-gram を 1 文字短くする locate = locate[l: ] # where==-l かっ locate=' ・では単に self. data[0] を取る - # ローテーションしているので、これは self. data のランダムな # アイテムである = self. data[where + len(locate) + 1] # 小文字アルファベットのみが欲しいので、他種のキャラクタを # 抜き出してしまった場合はランダムな文字を取る if not c. islower( ) : c = random. choice(string. lowercase) # 最後にこのキャラクタを self. chars に記録 self. chars. append(c) str_(self) : . join(self. chars) return ー d ef if name maln "Usage: pastiche [passwords [length [memory]]]" import sys
] 章テキスト = string. maketrans( ・ allchars = allchars. translate(allchars, keep. translate(allchars, delete)) delete def translate(s) : return s. translate(trans, delete) return translate 考察 私は文字列型の translate メソッドを使いたくなることがよくあるのだが、毎度毎度細部に迷って立ち止 まってしまう ( この「細部」については「レシピ 1.10 キャラクタセットを使い文字列をフィルタリング」を参 照 ) 。だからクラスを 1 っ書いて、簡単なうわべの下にさまざまな可能性を閉じ込めてやった ( のちにこれを 「解法」のクロージャに作り直した ) 。これにより、一定のキャラクタセットのみをキープするような関数が 〉〉 > trans('abcdefg ・ ) 〉〉〉 trans = translator(delete= ・ abcd ・ , keep='cdef' ) ( 両者がオーバーラップしたとき ) ようにするということだった・ ひとっ独断を下さざるを得ない設計選択があった。それは delete パラメータが keep パラメータを「負かす」 実に簡単。最後はちょっと特殊だったが、私のところでちよくちよく出てくるタスクだ。 て h ⅱ 5 Perkins : # # # - # # # > > > digits t0 hash( 'Chris Perkins : 224-7992 ' ) 〉〉 > digits t0 hash = translator(from=string ・ digits, t0 = ' # ・ ) キャラクタセットに属す文字をある 1 文字に置換したい場合も : て h ⅱ 5 Perkins ・ > > > no digits('Chris Perkins : 224-7992 、 ) > > > no digits = translator(delete=string. digits) あるキャラクタセットを除きたい場合も簡単だ・ 24799 〉〉 > digits only( 'Chris Perkins : 224-7992 ・ ) 〉〉〉 digits only = translator(keep=string. digits) 欲しいときに、簡単に作れるようになった・ のコードは通常の文字列にのみ作用し、 Unicode 文字列には使えない。この種の機能を Unicode 文字列に作 を調べる」や「レシピ 1.10 キャラクタセットを使い文字列をフィルタリング」にも記した通り、このレシピ そうだ ) などとした方が良いかもしれない。また、「レシピ 1.8 文字列にキャラクタセットの文字が含まれるか keep が指定されていたら delete を無視するとか、両方指定されていた場合には例外を出す ( こちらの方が良さ 両方指定されてるような場合はどうせちゃんとした意味を成さないかもしれないわけで、用途にもよるが、
XXIII 目次 訳者まえがき・・・ まえがき・ 1 章 テキスト・・ イントロダクション・・ 1.0 1.1 文字列を 1 文字すっ処理する・・ 1.2 キャラクタとその文字コード値を相互変換・・ オプジェクトが文字列のようなものかテストする・・ 1.3 1.4 文字列の整形・ 1.5 文字列両端のスペースを刈り込む・・ 1.6 文字列の連結・ 1.7 文字列を単語ごとに反転する・・ 文字列にキャラクタセットの文字が含まれるか調べる・・ 1.8 文字列クラスの translate メソッドを簡便に使う・・ 1.9 文字列をキャラクタセットでフィルタリング・・・ 1.10 文字列がテキストかノヾイナリ調べる・・ 1.11 大文字小文字・・・ 1.12 文字列の一部にアクセスする・・ 1.13 インデントの変更・・ 1.14 タブとスペースの変換・・ 1.15 文字列中の変数に手を加える・・・ 1.16 文字列中の変数に手を加える ( thon2.4 以降 ) ・ 1.17 一度に複数のパターンの置換をする・・ 1.18 文字列末尾が各種パターンと一致するか調べる・・ 1.19 Unicode により多言語テキストを扱う・・ 1.20 Unicode と通常の文字列を相互変換・・ 1.21 標準出力に Unicode キャラクタを出力・・ 1.22 1 「 / 一 8 一 9 “・ 1 1 ワ」 L.C -1 ・ 4 「 / 1 ワ」【 0 、 6 -8 一 -1 0 け「 / -1 」 1 -1 -1 1 -1 ワ」ワ〕ワ】ワ」 00 つけつけ -4 4 4 -4
4 1 章 テキスト 続きを待たねばならないこともある。データストリームが終了するまでは、このテキストは完全でない。こ のため、 makefile により生成した file オプジェクトは、そのままテキスト処理コードに渡せない場合がある。 ネットワークコネクションから来るテキストに対する作業では、処理に回す前にデータを読まねばならない ことが多々あり、またデータが大きければ、一度ファイルに落とすこともある。データが全部揃う前に処理 を開始せねばならないこともあるが、この場合には、もう少し精密な方法を使うとよい。これらの状況で便 利なパーサの例は、標準ライプラリのモジュール htmllib および HTMLP 訂 ser に見られる。 文字列の基本 テキスト処理の主力ツールとして thon が提供するのは、文字列 (strings)—キャラクタの不変 ( 変更不 能 ) シーケンス - ーーである。実のところ、文字列には 2 種類ある。片方は 8 ビットの ASCII キャラクタから成 る通常の文字列 ( プレーン文字列 ) で、もう一方は Unicode キャラクタから成る Unicode 文字列である。 Unicode 文字列については、こではあまり触れない。通常の文字列が 256 のキャラクタから成るのに対し、 Unicode 文字列ではキャラクタごとに 2 バイト ( あるいは 4 バイト ) 使用し、数万 ( あるいは数十億 ) のキャラクタが表 現できるというだけで、機能的には通常の文字列と変わらないからである。 Unicode 文字列が重要になるのは、 多種多様な文字体系、特にアジアの表意文字を扱わわねばならないときである。英語および非アジア系言語 の略式な文字セットを扱う場合は、通常の文字列で十分である。例えば西ヨーロッパの文字体系は、 ISO -885 1 ( ユーロ記号も使うなら ISO -8859-15 ) という国際標準ェンコードを使えば、全て通常の文字列に入れられる。 thon で、ありのままの文字列 ( lite stri 鷓 : 奇妙にも文字列リテラル (stringliteral) と言われることの方 クオートも、バックスラッシュでエスケープしなくとも、互いを内部に置けるというだけのことだ・ 文字列値の囲みは、シングルクオートでもダブルクオートでもよい。両者に違いはなく、 "this is another string' 'this is a literal string が多い ) を表すには、こうする : ただいすれの that spans two lines. "This is a long string\ 複数行にわたる文字列は、行末尾にバックスラッシュを置き、これにより続きの行があることを示してや "isn't that grand" ・ isn\'t that grand' that prints on two lines. "This is a long string\n\ 出力を 2 行にしたければ、ニューライン記号を埋め込まねばならぬ :
]. 9 文字列クラスの t 「 an 引 ate メソッドを簡便に使う一 長さであれば、 st て set. translate の実行でキャラクタが除外されなかったことになる、つまり strset のキャラ クタは astr に含まれていないことになる。逆にサプシーケンスが空であれば、すべてのキャラクタが除外さ れたということになる、つまり strset のすべてのキャラクタが astr に含まれていたということになる。 transla te メソッドはこのように文字列を集合として扱いたい場合の自然な選択肢となりつつあるが、これは高速な 上に手軽で柔軟だからである。「レシピ 1.10 文字列をキャラクタセットでフィルタリング」も参照されたい。 ここで示した 2 種のアプローチ群は、一般性に関して非常に異なるレベルにある。前者のアプローチはと ても一般性が高い。文字列処理に限ったものでなく、適用するオプジェクトへの依存性を最小にしている。 方 translate メソッドベースのアプローチは astr と strset の両者が文字列か、非常に文字列に近いものであ る場合にのみ使える。 Unicode でさえ十分ではない。 Unicode 文字列の translate メソッドはプレーン文字列 の translate とは異なるシグネチャ 引数が 2 つ ( 両者とも文字列 ) ではなく 1 っ ( コード番号を Unicode 文 字列または None にマッピングする dict) ーーを使っているからだ。 参照 「レシピ 1.10 文字列をキャラクタセットでフィルタリング」、ライプラリリファレンスおよび『巧 rthon ク イックリファレンス』にある文字列および Unicode オプジェクトの translate メソッドの解説、 string モジュー ルの maketrans 関数の解説、同上のモジュール sets(Python2.4 ではビルトイン set) 、 itertools 、特殊メソッ ド contains の解説。 1 .9 文字列クラスの translate メソッドを簡便に使う Credit: Chris Perkins, Raymond Hettinger 訳 : 鴨澤眞夫 問題 文字列クラスの tran 引 ate メソッドにある高速なコードがよく使いたくなるが、このメソッドや st 「 ing. maket 「 ans 関数の動作の詳細を憶えておけない気がする。だから通常の用途で簡単に使え るように、うわべを整えたい。 解法 「レシピ 1.10 キャラクタセットを使い文字列をフィルタリング」で詳しく書いたが、文字列型の translate メソッドは素晴らしく強力でフレキシプルだ。ところがまさにこの強力さとフレキシビリティのために、通 常の用途ではうわべに「化粧」を施してやるのが良いアイデアとなる。クロージャを返す小さなファクトリ 関数は、この種のタスクに驚異をもたらす。 import string def translator(frm=' if len(to) = delete=' , keep=None) : to = to * len(frm) = string. maketrans(frm, t0) trans if keep is not None:
]. 24 一部の文字列のみ大文字小文字無関係にする一 51 あらゆる Unicode キャラクタを扱える理想的なエンコーディングである。しかしユーザもアプリケーション も UTF -8 より ASC Ⅱやレⅱ n -1 を好むことが多い。 Unicode データに指定工ンコーディングを外れるキャラク タ (ASCII ではほとんどの記号やアクセント記号付きキャラクタが、ⅱ n -1 では「無限」記号がエンコードで きない ) が含まれていると、エンコーディングはこのデータを処理できない。 Python には xmlcharrefreplace と いうビルトインのエンコーディングエラー処理関数があり、エンコード不能なキャラクタ、たとえば無限記 号を、 & # 8734 のような XML の文字参照に置き換える。レシピではまた、同様のエラー処理関数で HTML 出 力を生成する、 html て eplace の書き方と登録の仕方を示した。 html_replace はエンコード不能のキャラクタを、 記号的で読みやすい HTML の実体参照、たとえば無限記号なら & infin ; を使って置き換える。 html replace は Unicode キャラクタのすべてをサポートしておらす、 HTML アプリケーション以外では使えないので、 xmlcharrefreplace より一般性が低い。しかし「 viewpage source 」の場合のように、できるだけ読みやすい HTML 出力が欲しい場合には、やはり有用だ。 出力が HTML でも他の XML フォームでもないなら、どちらの処理関数も意味がない。たとえは TeX やそ の他のマークアップ言語は XML の数値文字参照を解さない。とはいえ、こうしたマークアップ言語用に任意 のキャラクタへのリファレンスが構築できるなら、「解法」の html ー replace を改造してエラー処理関数を書き、 登録することは可能だ。 Unicode データを指定のエンコーディングとエラーハンドラでエンコードし、ファイルにまとめる方法は他 にも ( 非常に効果的なのが ) ある。 thon 標準ライプラリの codecs モジュールを使って : = codecs. open('out. html' mode=lw', encoding='ascii ・ errors=' html replace' ) outfile とすれば、任意の Unicode 文字列 unicode data に対して outfile. write(unicode data) が使える上に、エンコー ディングもエラー処理もすべてが透過的に行われるようになる。もちろん出力が終わったら outfile. close ( ) をコールすべきである。 参照 ライプラリリファレンスおよび『 python クイックリファレンス』のモジュール codecs および htmlentitydefs こうした文字列は str のサプクラスでラップするのが最適だろう : 解法 は元の状態のままで扱いたい。 文字列の一部を、比較や検索においては大文字小文字無関係 ( case - insensitive ) で、それ以外で 問題 Credit: DaIe Strickland-Clark, peter Cogo い Mark McMahon 訳 : 鴨澤眞夫 124 一部の文字列のみ大文字小文字無関係にする の解説。