CTC 教育サービス
[IT研修]注目キーワード Python UiPath(RPA) 最新技術動向 Microsoft Azure Docker Kubernetes
吉政創成 アシスタントの菱沼です。
今回も「いちばんやさしいPython入門教室(大澤 文孝氏著) 」を片手に勉強していきます。
さて、前回に引き続き、今回もヒット&ブローという数字当てゲームを作っていきます。
前回は4桁のランダムな数字の作り方と、リスト化まで行いました。今回は回答者側の処理部分で、まずは入力エラーの判定から学んでいきます。
1桁と4桁のランダムな数字の作り方はこちらです。
※1桁バージョンだけその後の動作もかかれています。(b=のところからが該当)
ランダムな数字を出すのは赤枠の部分が該当しています。
ヒット&ブローは桁ごとに正答を判断するため、その後の処理を楽にすることを考えて、桁ごとにランダムな数字を作ってあげる方式が取られています。また、値を一つの変数に入れると値をリストとして扱えるようなり、当てっこの時に処理がより楽になるそうです。水色で書いた[数字]はリストに振られるインデックス番号です。
さて、上記の後、回答に関係する処理が入っていくことになりますが、回答された内容がルールに従っているかをチェックするための処理を学びます。まずは桁数が4桁であるかを判定する処理です。
引用------------
P.135
まずは、4桁で入力されたということを確認してみましょう。Pythonではlen関数を使うことで、その文字列の長さを求められます。
次のように、len(b)が4ではない――すなわち、「if len(b) !=4:」という条件を指定すると、4桁で入力できたかどうかを確認できます。
>>> b = input(“数を入れてね>”)
>>>if len(b) ! = 4:
>>> print(“4桁の数字を入力してください”)
しかし、実際には4桁であることをチェックするだけではなく、4桁でなければ4桁がきちんと入力されるまで、繰り返し入力してほしいということがほとんどだと思います。そこで、whileを使ってループ処理するようにexample05-04-01.pyを以下のように書き換えます。
-----
ということで、次の画像がそれにあたります。
左側はユーザーに回答を入力させるだけの部分。中央が入力された値の桁数チェック部分、右側が実行結果です。確かに桁数が足りない場合にはエラーメッセージが出力され、4桁になるまで繰り返されています。
ちなみに「len(b) ! = 4:」の「! = 」は等しくないという意味を持つ比較演算子です。Pythonで使える比較演算子はこちらの記事もご参照ください。
さて次に、入力された文字がちゃんと数字であるかをチェックする方法です。
引用-----
P.138
各桁が数字であるということは、それぞれの桁が0以上9以下であるということです。よって次のように判定できます。
>>> if (b[0] >= “0”) and (b[0] <= “9”) :
ここで変数bのそれぞれの要素はユーザーが入力した「文字列」を切り出したもので「数字」ではありません。ですから、「”0”」とか「“9”」のように「” ”」でくくり、「文字列」として扱う点に注意してください。(中略)
さて本題は、正しく入力「されていない」ときにエラーを表示したいということです。そこでエラーを表示するには、これとは逆の条件となるよう、以下のようにします。
>>> if (b[0] < “0”) or (b[0] > “9” ) :
>>> print(“数字ではありません”)
------
b[0]は一桁目の数字をさすインデックス番号を使用しているので、4桁分を用意するにはそれぞれのインデックス番号を入れたもの(b[1]、b[2]、b[3])を用意してあげることになります。そうして先ほどの4桁かのチェックと組み合わせて出来上がったのがこちら。
elseの部分から同じ記述でちょっとうっとうしい…。インデックス番号が違うだけの同じものが並んでいるので省略できそうだけど…と思ったら、やっぱりできるようです。それがこちら。
なんかプログラミングっぽい!!という感動を覚えつつ。行数はそんなに変わりませんが、書かれている内容がすっきりしています。
最初はkazuokという変数にはtrueが入っています。これは始まった時点では入力された値が「数字」であるという前提でスタートし、実際に[0]から調べてみます。順に調べていった結果、文字が入力されていることが明らかになった時点で処理は停止し、「数字ではありません」という文字が表示されます。
一桁ずつ数字を調べるように書くのも面倒くさい場合には「正規表現」を使った「パターンマッチ」を行えばさらにすっきりできるらしいです。
引用-----
P.144
これをもっとスマートに書く方法として、正規表現を使うという方法があります。正規表現はパターンと呼ばれる書式を使って、文字列の頭から順に、文字がそのパターンと合致しているかどうかを調べる方法です。これをパターンマッチと言います。
数字かどうかを調べるには「\d」という特別な記号を使います。つまり先頭から「\d」が4つ繋がっているかどうかを調べることで、その文字列が4桁の数字であるかどうかを調べることができます。
------
さてそれが反映されたのがこちらです。
だいぶすっきりされていらっしゃる…。
>>> if not re.match(r”\d\d\d\d$”.b):
この一文だけで、4桁か、数字であるかの2つがチェックされています。
()内でそれが指定されているのだとは想像がつきますが、それぞれどんな意味があるのでしょうか。調べてみました。
re.matchの後に、「r”文字列”」(「R“文字列”」でもOK)がついています。
これはraw文字列と言うそうです。これをいれるとエスケープシーケンス(¥で表現される特別な文字)を無効化するものだそうです。そのエスケープシーケンスに当たるものは次の画像のものになります。
引用:2.4.1. 文字列およびバイト列リテラル|Python公式ドキュメント
参考:
正規表現の先頭につく`r`は何ですか?エスケープシーケンスやrow文字列を解説します|Python学習チャンネル by PyQ
「r」のあとに「“”」で文字列が囲まれた「¥d」が4つ書かれています。これは0~9までのどれかの数字を指す正規表現なのだそうで、4つ続いているということは4桁の10進数の数字という意味になります。
こういった正規表現には「¥(or バックスラッシュ)」が付きます。次に続く部分を「正規表現で書く」ときに、「r」を付けてあげると「\(or バックスラッシュ)」をそのままの存在として使うことができるようになるということと、わかりやすくなるということのようです。
他の正規表現についても調べたので、以下の表をご参照ください。実際に試せそうなものは試せる範囲で試してみました。
<特殊シーケンス>
Unicode |
ASCIIの場合 |
説明 |
注意事項 |
\d |
[0-9] |
0~9の数字 |
全角・半角数字の判別はしない |
\D |
[^0-9] |
数字以外の文字 |
全角・半角数字以外のすべての文字(記号、かな、カナ、英字等) |
\s |
[ \t\n\r\f\v] |
空白(スペース) |
全角・半角を判別しない |
\S |
[^ \t\n\r\f\v] |
\sの反対 |
全角・半角の空白以外のすべての文字(記号、かな、カナ、英字等) |
\w |
[a-zA-Z0-9_] |
ほとんどの文字、数字、アンダースコア( _ ) |
アンダースコア以外の記号はNG (全角アンダースコアはNG) |
\W |
[^a-zA-Z0-9_] |
\wの反対 |
半角、全角の記号(全角アンダースコアはOK) |
\Z |
$ |
文字列の末尾 |
|
<メタ文字>
記号 |
説明 |
書き方の例 |
例の記述で合致するもの |
. |
任意の一文字(改行除く) |
さ.え |
さざえ、さかえ、さむえ |
.. |
任意の二文字(改行除く) |
な..い |
なみへい、ないがい、ないせい |
^ |
行の先頭 |
^磯野 |
磯野なみへい、磯野ふね |
$ |
行の末尾 |
$磯野 |
なみへい磯野、ふね磯野 |
* |
0回以上繰り返す |
いっ*つも0点 |
いつも0点、いっつも0点、いっっつも0点… |
+ |
1回以上繰り返す |
いっ+つも0点 |
いっつも0点、いっっつも0点、いっっっつも0点… |
? |
0回か1回 |
いっ?つも0点 |
いつも0点、いっつも0点 |
{m} |
m回繰り返す |
0点{3} |
0点0点0点 |
{m,} |
m回以上繰り返す |
0点{2,} |
0点0点、0点0点0点、… |
{m,n} |
m~n回繰り返す |
0点{2,4} |
0点0点、0点0点0点、0点0点0点0点 |
a|b |
aかb |
たま|ハチ |
たま、ハチ |
[] |
[]内の値のどれか |
[0-9] |
0,1,2,3,4,5,6,7,8,9 |
[]{m} |
[]内の値のどれかをm回繰り返す |
[a-c]{5} |
abcab,abccb,bcabaなど |
参考:
【Python】とっても便利な正規表現!|Qiita @hiroyuki_mrp
分かりやすいpythonの正規表現の例|Qiita @luohao0404
書きながら覚えよう!Pythonで正規表現を使う方法【初心者向け】
[Python] 正規表現の表記方法のまとめ(reモジュール)|Hbk Project
次にreモジュールで使える関数はどんなものがあるかです。
この3つの違いについては「【python】re.match より re.search を使おう|Qiita @halhorn」がわかりやすいのでご参照ください。
完全一致ではなく、正規表現でマッチした部分を置換するときに利用。「subn」の方を使うと、置換した数を知ることができます。
どちらも検索した結果が表示されるものですが、取得方法がリストとイテレータで異なります。それぞれの違いについて「リストとイテレータの違いについて|teratail」の回答がわかりやすかったです。以下一部抜粋しましたが、より詳しい内容はリンクをご参照ください。
<一部抜粋>
イテレータは”状態を記憶する”ので、最後の要素を呼び出したら、次の要素はいません。という状態になり、このときにnext(i)で次を呼び出せばエラー(StopIteration)になります。
これはリストでも配列数よりも大きいIndexを指定すればエラー(IndexError)になるのと同じです。
文字列を分割する方法は複数あるそうですが、そのなかの一つがこの関数です。具体的なイメージと、その他の分割方法は「Pythonで文字列を分割(区切り文字、改行、正規表現、文字数)|note.nkmk.me」で説明されていました。
同じ正規表現パターンを繰り返して利用する場合、事前にコンパイルしておくと処理速度が速くなるそうです。flagsは省略可能で、<フラグ>の項目が入るようです。
<フラグ>
re.compileで利用できるフラグは全部で7種類のようです。ここでは一部だけ詳しめに書いて、他のものは関数名だけ書きます。「Pythonの正規表現で設定できるフラグの一覧|Let's プログラミング」のページで丁寧に解説されていましたので、ご参照ください。
ASCII文字のみにマッチするようになります。具体的には上表<特殊シーケンス>に記載した「ASCIIの場合」の書き方になります。(例:\d→[0-9])
ASCIIに変換されると、Unicodeの\wは「ほとんどの文字、数字、アンダースコア( _ )」にマッチングしましたが、ASCIIはa~z、A~Z、0~9になるので、ひらがななどの文字には反応しません。
\w → OK:katsuo、KATSUO、1234、かつお NG:!$%&
[a-zA-Z0-9_] → OK:katsuo、KATSUO、1234 NG:!$%&
例えば、「katsuo」「Katsuo」「KATSUO」のパターンがあった場合、このフラグを立てた場合は全てがマッチングします。
参考:
【python】re.match より re.search を使おう|Qiita @@halhorn
パターンから正規表現オブジェクトを作成する(Pattern)|Let's プログラミング
Pythonの正規表現で設定できるフラグの一覧|Let's プログラミング
正規表現|PythonエンジニアによるPython3学習サイト
Pythonの正規表現モジュールreの使い方(match、search、subなど)|note.nkmk.me
それでは今回はこちらで終了です。お付き合いいただきありがとうございました。
[IT研修]注目キーワード Python UiPath(RPA) 最新技術動向 Microsoft Azure Docker Kubernetes