静かなる名辞

pythonとプログラミングのこと



【python】pythonでscanf的なことをする

 一年以上前にこんな記事を書きました。

 これはこれで今読み返すと面白い(香ばしい)記事ですが、真剣にpythonでscanfと同じことをしたくてアクセスしてきた人には役に立たないという問題点がありました。

 そこで、pythonでscanfと同じことをする方法について真面目な記事を書いておきます。

 目次

 なお、本記事では次のコマンドで実行できる対話的インタプリタの出力を掲載します。返り値の文字列をそのまま出していますが、標準出力から出力したいときはprintを使ってください。

$ python


スポンサーリンク


何はともあれ入力を受け取る

 python2ではraw_input、python3ではinputという関数があり、標準入力からのキー入力を受け取ることができます。

 python2の場合

>>> s = raw_input()
hoge
>>> s
'hoge'

 python3の場合

>>> s = input()
hoge
>>> s
'hoge'

 この受け取った文字列を加工することで、なんとか所要を達するのが基本的なアプローチ方法になります。

文字列操作

 pythonには文字列の操作方法がいろいろあり、また文字列操作関数(メソッド)もいろいろあります。多すぎて網羅しきれないので、よく使うものだけまとめておきます。

>>> s = "hoge fuga" # 文字列の定義
>>> s[0] # 0文字目を取り出す
'h'
>>> s[-1] # 一番最後の文字を取り出す
'a'
>>> s[:4] # 0から3文字目を取り出す
'hoge'
>>> s[2:7] # 0から6文字目を取り出す
'ge fu'
>>> s.split(" ") # 半角スペースでsplitする
['hoge', 'fuga']
>>> s.replace("g", "G") # 文字列を置換する
'hoGe fuGa'
>>> s.isnumeric() # 数字の文字列かどうかを判定する
False

 なお、pythonはC言語と違って、「char型」というものはありません。1文字でもすべて「1文字の文字列」という扱いになります。

 このような文字列操作を型変換と組み合わせて使うことで、かなり色々なことができます。

>>> s = "1 1.5 1,5" # 文字列の定義
>>> splitted_s = s.split(" ") # とりあえずsplit
>>> splitted_s # splitした結果を表示
['1', '1.5', '1,5']
>>> d1 = int(splitted_s[0]) # splitした結果の0番目をint型にする 
>>> d1
1
>>> f1 = float(splitted_s[1]) # splitした結果の1番目をfloat型にする
>>> f1
1.5
>>> f2 = float(splitted_s[2].replace(",", ".")) # splitした結果の2番目をfloat型にする
>>> f2
1.5

 こういった処理をループなどと組み合わせて頑張れば、大抵のことは実現可能です。でも実際にやってみると頑張るのはなかなか辛いので、scanfのようなものがほしくなるのかもしれません。

正規表現を使う

 たとえば、こんな入力をした場合どうでしょう?

>>> s = "1, 1.5, 2, 2.5:hoge"

 この場合、一回のsplitで綺麗にsplitすることはできません。何回かに分けて繰り返しsplitするというのも一つの考え方ですが、こういう場合は正規表現を使うと簡単です。正規表現は標準ライブラリのreモジュールで使えます。

>>> s = "1, 1.5, 2, 2.5:hoge"
>>> import re
>>> re.split(r",\s+|:", s)
['1', '1.5', '2', '2.5', 'hoge']

 一発で綺麗にsplitできましたね。re.splitは区切り文字列の正規表現パターンと文字列を渡すと、それを探してそこで切ってくれます。

 ",\s+|:"というパターンの意味を簡単に説明すると、「カンマの後にスペースが続く OR コロン1つ」を区切り文字として使うという意味です。見た目よりは簡単な内容を表しているのがおわかり頂けるでしょう。それでいて、けっこう色々な表現ができます。

 正規表現はプログラミングをやるなら覚えておいて損はしないので、まだ使ったことがない人も使えそうな機会がある度に積極的に使って、少しずつでも覚えていくことをおすすめします。

parseライブラリを使う

 正規表現で複雑なsplitなどが簡単にできると言っても、けっきょく型変換は自分でやるしかないのが実情でした。これではscanfには及びません。

 そこで、parseというライブラリを使うことができます。これは外部ライブラリなので、pipで入れる必要があります。

$ pip install parse

 ※具体的なインストール方法は環境によって異なります。自分の環境でライブラリを入れる方法を確認した上で、正しい方法でインストールしてください。

 さっそく使ってみましょう。次のように使うことができます。

>>> parse.parse("{:d} hoge {:f}", "1024 hoge 11.19")[0]
>>> import parse
>>> s = input()
hoge 1024.20.48, 56
>>> parse.parse("hoge {:d}.{:f}, {:d}", s)
<Result (1024, 20.48, 56) {}>

 {:d}や{:f}がC言語でいうところのフォーマット文字列であり、scanfというかsscanfのように使うことができます。当然結果は型変換済みです。

 resultというオブジェクトが返っていますが、このオブジェクトはリストのようにインデックスでアクセスすることが可能です。

>>> result = parse.parse("hoge {:d}.{:f}, {:d}", s)
>>> result[0]
1024
>>> result[1]
20.48
>>> result[2]
56

 あるいは、結果を辞書のような形で格納することもできます。次のようにフォーマットを指定します。

>>> result = parse.parse("hoge {var1:d}.{var2:f}, {var3:d}", s)
>>> result["var1"]
1024
>>> result["var2"]
20.48
>>> result["var3"]
56

 なお、これらのリスト(厳密にはtuple)と辞書には、result.fixed, result.namedでアクセス可能です。

 parseライブラリはとても色々なことができます。より詳しく知りたい方は、parseライブラリの公式ページを読んでみてください。

GitHub - r1chardj0n3s/parse: Parse strings using a specification based on the Python format() syntax.

まとめ

 pythonにはそのままのscanfはありませんが、色々な手段で同じ目的を達することができます。最初は覚えるのが大変ですが、慣れてくると色々なことが簡単にできるのに気づいてくるはずです。Let's enjoy Python!