森の研究所で、ちくわ君は日本語のテキストデータに悪戦苦闘していた。机の上には赤い木の実とともに、漢字、ひらがな、カタカナが混在する文書が山積みになっている。
「ちくわさん、お疲れさまです!」はんぺん君が研究所に入ってきた。「今日は日本語の資料ですね」
「そうなんだ、はんぺん君」ちくわ君は困った表情を見せた。「日本語のテキスト処理は英語とは違って、特別な配慮が必要なんだよ。正規表現も日本語特有の書き方があるんだ」
「日本語の正規表現ですか?」はんぺん君は興味深そうに尋ねた。「普通の正規表現とは違うんですか?」
ちくわ君は木の実を一つ口に含んでから説明を始めた。「基本的な仕組みは同じだけれど、日本語には『ひらがな』『カタカナ』『漢字』『全角英数字』『半角英数字』など、多様な文字体系があるからね」
日本語文字の特徴を理解しよう
「まず、日本語で使われる文字の範囲を覚えよう」ちくわ君はホワイトボードに向かった。「ひらがなは『あ』から『ん』まで、カタカナは『ア』から『ン』まで、というのは知っているよね?」
「はい、それは知っています」はんぺん君は頷いた。
「正規表現では、これらを文字コード範囲で指定するんだ。ひらがなは『[あ-ん]』、カタカナは『[ア-ン]』と書けるよ」
ちくわ君は実際にPythonのコードを書き始めた。
import re
# ひらがなのマッチング
hiragana_pattern = r'[あ-ん]+'
text = "これはひらがなのテストです"
matches = re.findall(hiragana_pattern, text)
print("ひらがな:", matches)
# 出力: ['これはひらがなのテストです']
「おお!」はんぺん君は感動した。「本当にひらがなだけを抽出できるんですね」
カタカナを扱ってみよう
「今度はカタカナを試してみよう」ちくわ君は新しいコードを書いた。
# カタカナのマッチング
katakana_pattern = r'[ア-ン]+'
text = "コンピューターでプログラミングする"
matches = re.findall(katakana_pattern, text)
print("カタカナ:", matches)
# 出力: ['コンピューター', 'プログラミング']
「カタカナ語だけを抽出できました!」はんぺん君は興奮した。「これは外来語の分析に使えそうですね」
「その通り!でも注意点もあるんだ」ちくわ君は指摘した。「『ー』や『ッ』などの長音符や促音も含めたい場合は、パターンを調整する必要がある」
# 長音符と促音を含むカタカナパターン
katakana_extended_pattern = r'[ア-ンー・ッ]+'
text = "コンピューター、スマートフォン、インターネット"
matches = re.findall(katakana_extended_pattern, text)
print("拡張カタカナ:", matches)
# 出力: ['コンピューター', 'スマートフォン', 'インターネット']
漢字の処理
「漢字はもっと複雑なんだ」ちくわ君は新しい説明を始めた。「漢字の文字コード範囲は非常に広く、常用漢字だけでも2000文字以上ある」
「正規表現では、一般的に使われる漢字の範囲を『[一-龯]』で表現することが多いよ」
# 漢字のマッチング
kanji_pattern = r'[一-龯]+'
text = "今日は良い天気ですね"
matches = re.findall(kanji_pattern, text)
print("漢字:", matches)
# 出力: ['今日', '良', '天気']
「漢字だけが抽出されましたね」はんぺん君は感心した。「でも、『は』や『ですね』は抽出されていません」
「そう。この例では、助詞や語尾のひらがなは除外されているんだ。用途に応じて、パターンを調整する必要があるね」
複合パターンの作成
「実際の日本語テキストでは、ひらがな、カタカナ、漢字が混在している。これらを組み合わせたパターンを作ってみよう」ちくわ君は実践的な例を示した。
# 日本語文字全般のマッチング
japanese_pattern = r'[あ-んア-ン一-龯]+'
text = "私はプログラミングが好きです"
matches = re.findall(japanese_pattern, text)
print("日本語文字:", matches)
# 出力: ['私はプログラミングが好きです']
「でも、これだと文全体が一つのマッチになってしまいますね」はんぺん君は気づいた。
「いい観察だ!単語単位で抽出したい場合は、区切り文字を考慮する必要がある」ちくわ君は改良版を示した。
# 単語境界を考慮したパターン
word_pattern = r'[あ-んア-ン一-龯ー・ッ]+(?=[^あ-んア-ン一-龯ー・ッ]|$)'
text = "私は プログラミング が 好き です。"
matches = re.findall(word_pattern, text)
print("単語単位:", matches)
# 出力: ['私は', 'プログラミング', 'が', '好き', 'です']
郵便番号の抽出
「日本特有のパターンとして、郵便番号を抽出してみよう」ちくわ君は実用的な例を提示した。「日本の郵便番号は『123-4567』の形式だね」
# 郵便番号のマッチング
postal_code_pattern = r'\d{3}-\d{4}'
text = "住所は〒123-4567 東京都渋谷区です"
matches = re.findall(postal_code_pattern, text)
print("郵便番号:", matches)
# 出力: ['123-4567']
# 〒記号を含むパターン
postal_with_symbol_pattern = r'〒?\d{3}-\d{4}'
matches = re.findall(postal_with_symbol_pattern, text)
print("〒記号付き:", matches)
# 出力: ['〒123-4567']
「『〒』記号も正規表現で扱えるんですね!」はんぺん君は驚いた。
電話番号の日本語版
「日本の電話番号も特殊なパターンがあるよ」ちくわ君は続けた。「携帯電話、固定電話、フリーダイアルなど、様々な形式がある」
# 日本の電話番号パターン
phone_patterns = {
'mobile': r'0[789]0-\d{4}-\d{4}', # 携帯電話
'landline': r'0\d{1,4}-\d{1,4}-\d{4}', # 固定電話
'toll_free': r'0120-\d{3}-\d{3}' # フリーダイアル
}
text = """
連絡先:
携帯: 080-1234-5678
自宅: 03-1234-5678
フリーダイアル: 0120-123-456
"""
for phone_type, pattern in phone_patterns.items():
matches = re.findall(pattern, text)
print(f"{phone_type}: {matches}")
# 出力:
# mobile: ['080-1234-5678']
# landline: ['03-1234-5678']
# toll_free: ['0120-123-456']
日付の抽出
「日本語の日付表記は多様なんだ」ちくわ君は新しい例を示した。「『2024年6月17日』『令和6年6月17日』『6/17』など、様々な形式がある」
# 日本語の日付パターン
date_patterns = {
'western': r'\d{4}年\d{1,2}月\d{1,2}日',
'era': r'[平成令和]\d{1,2}年\d{1,2}月\d{1,2}日',
'slash': r'\d{1,2}/\d{1,2}',
'kanji_month': r'\d{1,2}月\d{1,2}日'
}
text = """
会議の予定:
2024年6月17日(月)
令和6年6月18日(火)
6/19(水)
6月20日(木)
"""
for date_type, pattern in date_patterns.items():
matches = re.findall(pattern, text)
print(f"{date_type}: {matches}")
# 出力:
# western: ['2024年6月17日']
# era: ['令和6年6月18日']
# slash: ['6/19']
# kanji_month: ['6月18日', '6月20日']
「同じテキストから、いろんな日付形式を抽出できるんですね」はんぺん君は感心した。
価格と通貨の抽出
「日本語テキストでよく出てくる価格表記も処理してみよう」ちくわ君は実践的な例を続けた。
# 価格パターンの抽出
price_patterns = {
'yen_symbol': r'¥[\d,]+',
'yen_kanji': r'[\d,]+円',
'man': r'[\d,]+万円',
'oku': r'[\d,]+億円'
}
text = """
商品価格:
ノートPC: ¥89,800
スマートフォン: 128,000円
車: 300万円
会社の売上: 50億円
"""
for price_type, pattern in price_patterns.items():
matches = re.findall(pattern, text)
print(f"{price_type}: {matches}")
# 出力:
# yen_symbol: ['¥89,800']
# yen_kanji: ['128,000円']
# man: ['300万円']
# oku: ['50億円']
敬語の検出
「もう少し高度な例として、敬語表現を検出してみよう」ちくわ君は言語学的な応用を示した。「これは文章の丁寧さレベルを判定するのに使えるよ」
# 敬語パターンの検出
keigo_patterns = {
'teineigo': r'です|ます|である', # 丁寧語
'sonkeigo': r'いらっしゃる|おっしゃる|なさる', # 尊敬語
'kenjougo': r'いたします|させていただく|申し上げる' # 謙譲語
}
text = """
お客様がいらっしゃいました。
私が対応いたします。
ご質問をおっしゃってください。
"""
for keigo_type, pattern in keigo_patterns.items():
matches = re.findall(pattern, text)
if matches:
print(f"{keigo_type}: {matches}")
# 出力:
# teineigo: []
# sonkeigo: ['いらっしゃ', 'おっしゃ']
# kenjougo: ['いたし']
「テキストの丁寧さレベルを自動判定できるんですね!」はんぺん君は驚いた。
文字種の統計を取ろう
「実用的な応用として、テキストの文字種統計を取ってみよう」ちくわ君は分析ツールを作り始めた。
# 文字種統計の計算
def analyze_japanese_text(text):
patterns = {
'ひらがな': r'[あ-ん]',
'カタカナ': r'[ア-ン]',
'漢字': r'[一-龯]',
'英数字': r'[a-zA-Z0-9]',
'記号': r'[、。!?]'
}
results = {}
total_chars = len(text)
for char_type, pattern in patterns.items():
matches = re.findall(pattern, text)
count = len(matches)
percentage = (count / total_chars) * 100 if total_chars > 0 else 0
results[char_type] = {
'count': count,
'percentage': round(percentage, 1)
}
return results
# テスト用テキスト
sample_text = "今日はプログラミングの勉強をしました。正規表現は便利ですね!"
stats = analyze_japanese_text(sample_text)
for char_type, data in stats.items():
print(f"{char_type}: {data['count']}文字 ({data['percentage']}%)")
# 出力例:
# ひらがな: 15文字 (48.4%)
# カタカナ: 8文字 (25.8%)
# 漢字: 6文字 (19.4%)
# 英数字: 0文字 (0.0%)
# 記号: 2文字 (6.5%)
実習:ブログ記事の分析
「実際のブログ記事を分析してみよう」ちくわ君は総合的な演習を提案した。「様々な情報を抽出してみて」
# ブログ記事分析の総合例
blog_text = """
投稿日: 2024年6月17日
タイトル: 今日のプログラミング学習
値段: ¥3,200の本を買いました
連絡先: メール info@example.com 電話 03-1234-5678
今日はPythonの正規表現について学びました。
とても便利な機能ですね!
特に日本語処理に役立ちそうです。
#プログラミング #Python #正規表現
"""
# 包括的な分析関数
def analyze_blog_post(text):
analyses = {}
# 日付の抽出
dates = re.findall(r'\d{4}年\d{1,2}月\d{1,2}日', text)
analyses['日付'] = dates
# 価格の抽出
prices = re.findall(r'¥[\d,]+', text)
analyses['価格'] = prices
# メールアドレスの抽出
emails = re.findall(r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}', text)
analyses['メール'] = emails
# 電話番号の抽出
phones = re.findall(r'\d{2,4}-\d{1,4}-\d{4}', text)
analyses['電話番号'] = phones
# ハッシュタグの抽出
hashtags = re.findall(r'#[^\s]+', text)
analyses['ハッシュタグ'] = hashtags
return analyses
# 分析実行
results = analyze_blog_post(blog_text)
for category, items in results.items():
if items:
print(f"{category}: {items}")
# 出力:
# 日付: ['2024年6月17日']
# 価格: ['¥3,200']
# メール: ['info@example.com']
# 電話番号: ['03-1234-5678']
# ハッシュタグ: ['#プログラミング', '#Python', '#正規表現']
「すごいですね!一つのテキストからこんなにたくさんの情報を抽出できるなんて」はんぺん君は感動した。
注意点と限界
「日本語の正規表現には注意点もあるんだ」ちくわ君は木の実を噛みながら説明した。「文字エンコーディングに気をつける必要がある。UTF-8を使うのが一般的だけれど、古いシステムではShift_JISやEUC-JPを使っていることもある」
「それと、漢字の読みは正規表現では判定できない。『山田』と『やまだ』は別の文字として扱われるからね」
「形態素解析という別の技術と組み合わせると、より高度な日本語処理ができるようになるよ」
まとめの時間
夕日が研究所を照らし始めた頃、はんぺん君は満足そうに立ち上がった。「今日は日本語の正規表現について本当にたくさん学べました!日本語特有の文字体系を理解して処理することが大切なんですね」
「そうだね。日本語は複雑な言語だけれど、正規表現を使いこなせれば、とても豊富な情報を抽出できるよ」ちくわ君は微笑んだ。「ビジネス文書の分析、SNSの投稿解析、Webスクレイピングなど、応用範囲は広いからね」
「家に帰ったら、早速日本語のテキストで練習してみます!」はんぺん君は意気込んだ。
「それは素晴らしい!最初は新聞記事やブログ記事から始めて、徐々に複雑なテキストに挑戦していこう。日本語の正規表現をマスターすれば、きっと君のデータ分析スキルが大幅に向上するよ」
はんぺん君が帰った後、ちくわ君は日本語テキストの分析に戻った。正規表現を使って、効率的に必要な情報を抽出し、美しいレポートを作成することができた。
森の研究所には、今日も日本語データサイエンスの知恵が満ちていた。