横山ホットブラザーズ師匠のネタでPython文字列操作を学ぶ

のこぎり Python初心者
スポンサーリンク

こんばんわ、ときか姉です♪

今日はPythonの文字列操作について書いてみたいと思います。
私は現在はPythonでしかコーディングする機会がありませんが、かつて使っていたC言語やMATLABから移行したてのころは、インデックス指定で直接代入できないことに混乱したこともありました。

このPython文字列操作、意外とつまづきやすい点もあるので、初心者向けにまとめてみました。初歩的すぎて大体知ってるよ、という方も多いと思いますので、そういう方は最後だけでも見て行ってください。

以下では、ときか姉が敬愛してやまない横山ホットブラザーズのあのネタを例としてPython文字列操作を学んでいきたいと思います。

基本的な処理

以下の文字列を用いて、様々な文字列処理を行ってみます。
(注:本稿の例として用いる文字列はあえて全角で統一しました。半角にすると機能しませんのでご注意ください。@も全角です。)

s='お〜ま〜え〜は〜あ〜ほ〜か〜@横山ホットブラザーズ' #今回用いる文字列print(s) # 文字列そのものを表示

>> お〜ま〜え〜は〜あ〜ほ〜か〜@横山ホットブラザーズ

文字列の分割と置換

この名セリフをそのまま口ずさめれば一番いいのですが、常に都合良く手元に楽器、ましてやのこぎりがあるとは限りません。その場合は’〜’のように伸ばしているヒマはないので、手短かに伝える必要があります。そのような場合は文字列から’〜’を消す、つまり”と置き換える必要があります。文字の置換にはreplaceメソッドを用います。

print(s.replace('〜','')) # 前が置換前, 後ろが置換後の文字列

>> おまえはあほか@横山ホットブラザーズ

短くなりました。短くなったことによってわかると思いますが、一般にトゲのあるこのセリフを穏やかな笑いに変えてこられた師匠の音楽センスと人徳には舌を巻くばかりです。
尚この文字列は、‘セリフ@発言者’の並びになっています。このように’@’によって区切られた文字列としてはメールアドレスなどが代表的ですが、エンジニアはよく遭遇する文字列かと思います。

今度はこれらを「セリフ」「発言者」に分離してさらに以下三点の変更を行ってみます。
・セリフを引用(「」くくり)に変更
・誰の発言かわかるように’@’を’by’に変更
・横山ホットブラザーズを横山ホット「シスターズ」(実在)に変更

ここでの’@’のように、特定の文字で文字列を区切るにはsplitメソッドを用います。

s0=s.split('@')[0].replace('〜', '') # '@'で区切った0番目配列に置換を適用
print(s0)

>> おまえはあほか

引用で括るには、文字列(’「」‘との)の結合です。結合は‘+’で行えます。

s0='「' + s0 + '」' # 上で抽出したセリフの両端に引用符を追加
print(s0)

>> 「おまえはあほか」

そして、先程同様replaceメソッドを用いて、後半の「ブラザーズ」を「シスターズ」に置き換えます。

s1=s.split('@')[1].replace('ブラザーズ', 'シスターズ') # 置換
print(s1)

>> 横山ホットシスターズ

 あとは、’@’をreplaceメソッドで’by’に置き換えるだけです。
この置き換えを上記二つの変更とともに一気に行ってみましょう。
以下のようになります。

s01='「' + s.split('@')[0].replace('〜', '')
+ '」' + ' by ' + s.split('@')[1].replace('ブラザーズ', 'シスターズ')
print(s01)

>> 「おまえはあほか」 by 横山ホットシスターズ

いかがでしょうか?基本はreplace/splitメソッド、+による文字列の結合のみです。

ここまで真面目に読んでくださった方ありがとうございます。
簡単すぎて読んで損したと思わないために、プラスアルファのテクニックを以下に書きます。騙されたと思って読んでください。きっと騙されます。

少しだけ高度な処理

アルゴリズムテストやコーディング能力チェック問題などでありがちですが、「ある文字が出るたびにカウントした整数を代入していく」などの処理が必要な場合がありえます。このような問題に対応するのは少々複雑です。上述のやり方を知っているだけでは難しいです。

例題として今度は以下の文字列を考えてみましょう。

「’ド#ド#ド#シbド#シbド#’」

お察しの通り、これは「お〜ま〜え〜は〜あ〜ほ〜か」のメロディです。

s='ド#ド#ド#シbド#シbド#'
print(s) # 文字列sの表示
print(s.count('#')) # '#'の個数を数える
print(s.count('b')) # 'b'の個数を数える

>> ド#ド#ド#シbド#シbド#
>> 5 
>> 2

上記文字列には’#’が5つ含まれています。この文字列に対して以下のような処理を行う問題を考えてみます。
[1] この’#’を「全て」’X’に置換する
[2] この’#’のうち、前からY番目「だけ」を’X’に置換する
[3] この’#’を「前から順番にX個目の’X’に」置換する

[1]はとても簡単です。replaceメソッドを用いるだけで一括で行えます。

print(s.replace('#','X'))

>> ドXドXドXシbドXシbドX

[2]はどうでしょうか?最初の’#’だけを’X’に置換してみましょう。
最初の’#’は文字列sの二番目ですので、ゼロから始まるpythonインデックスとしては1となります。

s[1]='X' # 文字列sの1番目に'X'を代入

とすればよさそうに思えます。しかし実際に行うと以下のようなエラーが出てしまいます。

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-45-a5a43bf77bec> in <module>
----> 1 s[1]='X'

TypeError: 'str' object does not support item assignment

このように、Pythonにおいては、文字列をインデックス指定でそのまま代入ができないようになっています。なので以下のように少し工夫が必要です。

ss=s[:1]+'X'+s[2:] # 置換する文字列と置換しない元の文字列を連結
print(ss)

>> ドXド#ド#シbド#シbド#

ちょっとめんどくさいですよね。
i番目を指定して置換するには以下のように書くのも手です。

i=3 # i番目を指定
ss=s[:i]+'X'+s[i+1:] # 置換する文字列と置換しない元の文字列を連結
print(ss)

>> ド#ドXド#シbド#シbド#

[3]を行うとなるともう少し複雑になります。以下のようにfor文とif文を用いるとできそうです。

s='ド#ド#ド#シbド#シbド#' # 対象となる文字列
for i in range(len(s)): # 文字列sの長さでfor文を回す
   if s[i]=='#': # i番目の文字が'#'ならば
       s=s[:i]+str(i)+s[i+1:]
print(s)

>> ド1ド3ド5シbド9シbド13

出力を見ると様子がおかしいです。置換する数がとびとびになっています。for文のiごとに増やしているからですね。適当なカウンターを追加しましょう。

s='ド#ド#ド#シbド#シbド#'
cntr=1
for i in range(len(s)):
   if s[i]=='#':
       s=s[:i]+str(cntr)+s[i+1:]
       cntr+=1
print(s)

>> ド1ド2ド3シbド4シbド5

無事狙い通りの出力を得ることができました。
まだまだ冗長ですので、さらにシンプルな記述に修正したいと思います。まず、置き換えるために’#’の個数まで純増していく数字から成る文字列tupleを取得します。これは以下のように一行で書けます。

n=tuple([str(i+1) for i in range(s.count('#'))]) # 1〜5まで数から成るtupleをラムダ式で作成
print(n)

>> ('1', '2', '3', '4', '5')

そして、この数を’#’に対応するところに挿入すれば良いです。挿入元が単数ではなく複数あり、それらを展開して挿入したい場合には’%’を用います。

ss=s.replace('#', '%s') # 置換したい'#'を'%s'に置き換える
print(ss)
print(ss % (n)) # '%s'の箇所にnを展開して挿入

>> ド%sド%sド%sシbド%sシbド%s
>> ド1ド2ド3シbド4シbド5

このやり方だと二行で書けてしまいます。可読性が下がるので無理をする必要はありませんが、これをまとめて一行で書くこともできます。

print(s.replace('#','%s') % (tuple([str(i+1) for i in range(s.count('#'))])))
>> ド1ド2ド3シbド4シbド5

以上です。書き方はもちろん上記の限りでありませんが、文字列操作理解の入門としては参考になるかと思います。

データサイエンティスト初心者が作業効率をアップするファーストステップとして、各種データ整形・前処理に必要な以下の二点が必須かと個人的には思っています。
 A. numpyのスライシングが遅延なしでできる
 B. pandasの要素抽出と基本統計描画を自在に行える

本稿ではAと関連して文字列のインデキシングとnumpyのスライシングの両方を書こうと思っていたのですが、前者だけでも思いの外長くなってしまいましたので、後者はまた日を改めて書くことにしますね。

楽しんでいただけましたら幸いです♪

♪♪♪ Have a nice coding day ♪♪♪

[トレード口座開設はコチラ↓]

コメント

タイトルとURLをコピーしました

Fatal error: Uncaught JSMin_UnterminatedStringException: JSMin: Unterminated String at byte 821: "「お〜ま〜え〜は〜あ〜ほ〜か?」でPythonの文字列操作を学びます♪ in /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/external/php/jsmin.php:214 Stack trace: #0 /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/external/php/jsmin.php(152): JSMin->action(1) #1 /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/external/php/jsmin.php(86): JSMin->min() #2 /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/external/php/ao-minify-html.php(257): JSMin::minify('\n{\n "@context"...') #3 [internal function]: AO_Minify_HTML->_removeScriptCB(Array) #4 /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/external/php/ao-minify-html.php(108): preg_replace_callback('/(\\s*)(<script\\...', Array, '<!doctype html>...') #5 /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/external/php/ao-minify-html.php(47): AO_Minify_HTML->process() #6 /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/autoptimizeHTML.php(105): AO_Minify_HTML::minify('<!doctype html>...', Array) #7 /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/autoptimizeMain.php(592): autoptimizeHTML->minify() #8 [internal function]: autoptimizeMain->end_buffering('<!doctype html>...', 9) #9 /home/tokikaneesan/tokikaneesan.com/public_html/wp-includes/functions.php(5420): ob_end_flush() #10 /home/tokikaneesan/tokikaneesan.com/public_html/wp-includes/class-wp-hook.php(324): wp_ob_end_flush_all('') #11 /home/tokikaneesan/tokikaneesan.com/public_html/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters('', Array) #12 /home/tokikaneesan/tokikaneesan.com/public_html/wp-includes/plugin.php(517): WP_Hook->do_action(Array) #13 /home/tokikaneesan/tokikaneesan.com/public_html/wp-includes/load.php(1270): do_action('shutdown') #14 [internal function]: shutdown_action_hook() #15 {main} thrown in /home/tokikaneesan/tokikaneesan.com/public_html/wp-content/plugins/autoptimize/classes/external/php/jsmin.php on line 214