Unicode 空白文字(Zs)の処理:Python の unicodedata を利用した正規化

Unicode にはさまざまな空白文字があり、文字列に混ざっていると厄介な問題が起きるリスクがあります。

import unicodedata

space_separators = [
	('\u0020', 'SPACE'),  # 通常のスペース
	('\u00A0', 'NO-BREAK SPACE'),  # ノーブレークスペース
	('\u1680', 'OGHAM SPACE MARK'),  # オガム文字スペース
	('\u2000', 'EN QUAD'),  # en quad
	('\u2001', 'EM QUAD'),  # em quad
	('\u2002', 'EN SPACE'),  # en space
	('\u2003', 'EM SPACE'),  # em space
	('\u2004', 'THREE-PER-EM SPACE'),  # 1/3 em space
	('\u2005', 'FOUR-PER-EM SPACE'),  # 1/4 em space
	('\u2006', 'SIX-PER-EM SPACE'),  # 1/6 em space
	('\u2007', 'FIGURE SPACE'),  # 数字幅スペース
	('\u2008', 'PUNCTUATION SPACE'),  # 句読点幅スペース
	('\u2009', 'THIN SPACE'),  # thin space
	('\u200A', 'HAIR SPACE'),  # hair space
	('\u202F', 'NARROW NO-BREAK SPACE'),  # 狭いノーブレークスペース
	('\u205F', 'MEDIUM MATHEMATICAL SPACE'),  # 数学用中スペース
	('\u3000', 'IDEOGRAPHIC SPACE'),  # 全角スペース(日本語)
]

for char, name in space_separators:
	print(f'{unicodedata.category(char)} {unicodedata.name(char)}')

# Zs SPACE
# Zs NO-BREAK SPACE
# Zs OGHAM SPACE MARK
# Zs EN QUAD
# Zs EM QUAD
# Zs EN SPACE
# Zs EM SPACE
# Zs THREE-PER-EM SPACE
# Zs FOUR-PER-EM SPACE
# Zs SIX-PER-EM SPACE
# Zs FIGURE SPACE
# Zs PUNCTUATION SPACE
# Zs THIN SPACE
# Zs HAIR SPACE
# Zs NARROW NO-BREAK SPACE
# Zs MEDIUM MATHEMATICAL SPACE
# Zs IDEOGRAPHIC SPACE

Zs カテゴリーにある空白文字をいつもの半角スペースまたは日本語の全角スペースに統一したい、という場合、どうすればいいでしょうか。

import unicodedata


def is_unicode_whitespace(char):
	return unicodedata.category(char).startswith('Z')


a = is_unicode_whitespace('')
b = is_unicode_whitespace(' ')

print(a)  # False
print(b)  # True

category は unicode character を引数とするため、is_unicode_whitespace は二文字以上の判定には使えません。

import unicodedata


def is_unicode_whitespace(char):
	return unicodedata.category(char).startswith('Z')


a = is_unicode_whitespace('あい')

# TypeError: category() argument must be a unicode character, not str

文字列にあるさまざまな空白文字を一括で同じものにするには、次のようにします。

import unicodedata


def is_unicode_whitespace(char):
	return unicodedata.category(char).startswith('Z')


def replace_all_unicode_whitespace(text, replacement=' '):
	if not text:
		return text

	result = []

	for char in text:
		if is_unicode_whitespace(char):
			result.append(replacement)

		else:
			result.append(char)

	return ''.join(result)


s = 'あい うえお'
t = replace_all_unicode_whitespace(s, ' ')

print(s)  # あい うえお
print(t)  # あい うえお