skip to content
barorin&

Pythonの文字列処理まとめ|正規表現・JSONの文字化け・ハッシュ値

/ 7 min read

Table of Contents

はじめに

文字列処理は、データを扱っていれば必ず通る領域です。この記事では、正規表現・JSON・ハッシュ値まわりで実際に使う処理をまとめました。

re.findall で文字列を抽出する

re.findall() は、パターンに一致した部分をすべてリストで返します。

import re
s = "I love penguins. I love bears."
re.findall(r"I love penguins.", s)
# ['I love penguins.']

丸括弧の有無で結果が変わる

ここが findall のいちばん重要な仕様です。

# 括弧なし: 一致した全体が返る
re.findall(r"I love penguins.", s)
# ['I love penguins.']
# 括弧あり: 括弧の中だけが返る
re.findall(r"I love (penguins).", s)
# ['penguins']
# (?:...) はグループ化するが抽出はしない
re.findall(r"I love (?:penguins|bears).", s)
# ['I love penguins.', 'I love bears.']

(...)キャプチャグループといい、findall はそこだけを取り出します。一方 (?:...)非キャプチャグループで、| を使うために括る場合など「まとめたいだけ」のときに使います。

意図せず括弧を使うと結果が変わってしまうので、抽出したい部分だけを (...) で囲むのが基本です。

AND条件とOR条件を書き分ける

正規表現では、この2つの書き方がまったく違います。

OR条件(どれかに一致)

| で区切ります。

re.findall(r"ねこ|いぬ", "ねこといぬ")
# ['ねこ', 'いぬ']

AND条件(すべてを含む)

| ではAND条件になりません。先読み (?=...) を重ねます。

import re
# 「ねこ」と「いぬ」の両方を含むか
pattern = r"(?=.*ねこ)(?=.*いぬ)"
bool(re.search(pattern, "ねこといぬ")) # True
bool(re.search(pattern, "ねこだけ")) # False

(?=...) は「この位置から先に、この条件を満たす部分がある」という判定だけを行い、文字を消費しません。そのため複数重ねると「すべて満たす」という意味になります。

文字の重複がない単語を抽出する

すべての文字が1回ずつしか現れない単語を取り出します。

words = ["about", "is", "cat", "skill", "test"]
[w for w in words if len(set(w)) == len(w)]
# ['about', 'is', 'cat']

set() は重複を除いた集合を作るので、元の長さと一致すれば重複がないと判定できます。1行で済むうえに意図も読み取りやすい書き方です。

日本語のJSONが文字化けする

日本語を含む辞書を json.dumps() すると、\uXXXX 形式にエンコードされます。

import json
j = [{"項目名": "値"}]
print(json.dumps(j))
# [{"\u9805\u76ee\u540d": "\u5024"}]

ensure_ascii=False を付ければ、そのまま出力されます。

print(json.dumps(j, ensure_ascii=False))
# [{"項目名": "値"}]

ファイルに書き出すときも同様です。読みやすく整形する indent と併せて指定することが多いです。

with open("data.json", "w", encoding="utf-8") as f:
json.dump(j, f, ensure_ascii=False, indent=2)

ハッシュ値を生成する

hashlib を使います。文字列は先にバイト列へ変換します。

import hashlib
value = "hogefuga"
hashlib.md5(value.encode()).hexdigest()
hashlib.sha256(value.encode()).hexdigest()

アルゴリズム名を変えるだけで切り替えられます。

ファイルのハッシュ値を取る場合は、メモリに全部載せずに少しずつ読み込みます。

import hashlib
def file_hash(path):
h = hashlib.sha256()
with open(path, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()

おわりに

正規表現は、findall の括弧の扱いと、AND条件が | では書けないという2点を押さえておけば、大半の場面で困りません。凝ったパターンを1つ書くより、Pythonの条件式に分けたほうが読みやすくなることも多いので、無理に正規表現だけで解こうとしないのがコツです。

この記事を書いた人

barorinのプロフィール画像

barorin公認会計士

会計とITの二足のわらじで働く公認会計士です。Pythonを中心に、会計・監査の実務で実際に使っているコードや、Ubuntu・Raspberry Piの設定メモを書いています。