ゼロから学ぶ正規表現による
中国語テキスト検索・分析入門
Pulsar エディタ・Python・jiebaを使って、自分でデータを作り、自在に検索・分析する
本書は、中国語の研究・学習に関心を持つ方が、自分でデータを集め、整形し、正規表現を使って検索・分析できるようになることを目指した実践的なテキストです。プログラミングの知識がなくても第4章まで学べます。Pythonは第5章以降で、必要な場面で少しずつ紹介します。
1.1 正規表現って何?
正規表現(Regular Expression、略して regex)とは、文字列のパターンを記号で表現する方法です。「任意の1文字」「0回以上の繰り返し」「行の先頭」といった条件を、特定の記号(メタ文字)で書き表します。
たとえば「結婚という2文字の間に何か1文字入っているもの」を探したいとき、正規表現では:
.(ピリオド)が「任意の1文字」を表しています。このように、記号1つで「どんな文字でもいい」という条件を表現できるのが正規表現の力です。
1.2 何ができるのか・できないこと
できること
- 柔軟な検索:完全一致でなく「パターン」で探す
- 一括置換:データの整形・クリーニング
- 抽出:本文・セリフ・特定構造の文だけを取り出す
- 複数ファイル横断検索(Grep):数百ファイルを一度にスキャン
- 繰り返し処理の自動化:Pythonと組み合わせれば何千ファイルも
苦手なこと・できないこと
- 意味的な検索:「怒りを表す表現」などは意味を理解できない
- 深いネスト構造の完全解析:HTMLの複雑な入れ子には限界がある
- 完璧な精度:意図しないものがヒットすることもある(試行錯誤が必要)
1.3 Ctrl+F との違い
1.4 なぜ中国語研究に向いているのか
英語はスペースで単語が区切られているため、単語単位の検索が比較的簡単です。しかし中国語はスペースで区切られていません。「结」という文字が「结婚」の一部なのか「结束」の一部なのか、文字だけでは判断できません。
また中国語には離合詞(「结婚」→「结了婚」)、呼応形式(「虽然…但是」)、重ね型(「看看」「高高兴兴」)など、パターン的な文法形式が豊富です。これらはまさに正規表現が得意とする「構造的なパターンの検索」に向いています。
2.1 インストールと設定
本書ではPulsar(旧Atom)を使います。無料・オープンソースで、Windows・Mac・Linux すべてに対応しています。
- https://pulsar-edit.dev/ にアクセス
- お使いのOSに合わせてダウンロード
- インストーラーを実行(指示に従うだけ)
- 起動後、
File → Add Project Folderでデータフォルダを開く
2.2 画面の説明
. や * が記号ではなくそのままの文字として検索されます。必ずオンにしてから使いましょう。2.3 最初の検索を試す
ステップ1:サンプルテキストを作る
メモ帳やPulsarで以下の内容を sample.txt(UTF-8)として保存してください:
他是昨天去的北京。 我是在上海出生的。 她是坐飞机来的。 这是我的书。 我们是好朋友。
ステップ2:検索してみる
- Pulsarで
sample.txtを開く Ctrl+F(Mac: Cmd+F)で検索パネルを開く.*ボタンをクリックして正規表現モードをオン- 検索欄に
是.+的と入力 - ハイライトされた部分を確認する
是.+的 を検索すると何行ヒットしますか。^我 を検索すると何行ヒットしますか(^ の意味を考えてみましょう)。。$ を検索すると何行ヒットしますか。3.1 任意の1文字:.(ピリオド)
. は「改行以外の任意の1文字」にマッチします。
| 検索式 | 意味 | マッチ例 | 非マッチ例 |
|---|---|---|---|
结.婚 | ちょうど1文字 | 结了婚、结过婚 | 结婚、结了两次婚 |
这.是 | ちょうど1文字 | 这不是、这就是 | 这是 |
一..二 | ちょうど2文字 | 一干二净、一来二去 | 一二、一不二 |
结...婚 | ちょうど3文字 | 结了两次婚 | 结了婚 |
3.2 繰り返し:* + ? {n,m}
3.3 文字クラス:[] と [^]
[] は「この中の文字のどれか1文字」にマッチします。
[^,。?!]+ は「句読点をまたがせない」ための最重要パターンです。「このフレーズは1つの文の中に収まっているはず」という制限を加えるときに使います。第4章で頻繁に登場します。| 書き方 | 意味 | 例 |
|---|---|---|
[啊吧呢] | このうちの任意の1文字 | 啊か吧か呢 |
[0-9] | 半角数字1文字(範囲指定) | 0〜9のどれか |
[a-zA-Z] | 英字1文字 | aからzまたはAからZ |
[一-龠] | 漢字1文字(概算範囲) | 漢字のおおよその範囲 |
[^,。?!] | 句読点以外の1文字 | 句点が出るまでマッチ |
[^0-9] | 数字以外の1文字 | 数字でなければ何でも |
3.4 行頭・行末:^ と $
| 記号 | 意味 | 使用例 | 何にマッチするか |
|---|---|---|---|
^ | 行の先頭 | ^" | 引用符で始まる行(セリフ行の抽出) |
$ | 行の末尾 | 。$ | 句点で終わる行 |
^\n | 空行 | ^\n | 空行(削除するときに使う) |
^不 | 行頭が「不」 | ^不 | 否定文の行 |
3.5 グループ・選択・後方参照:() | \1 $1
・検索ボックス:
\1(バックスラッシュ+数字)・置換ボックス:
$1(ドル記号+数字)混同しやすいので注意してください。
置換での後方参照の使い方
| 検索 | 置換 | 効果 |
|---|---|---|
([。!?]) | $1\n | 句点の後に改行を挿入(1文1行化) |
<b>(.+?)</b> | $1 | <b>タグを除去してテキストだけ残す |
(\S+)\s(\S+) | $2 $1 | 空白区切りの2語を入れ替え |
3.6 貪欲マッチと非貪欲マッチ
これは正規表現で最もつまずきやすいポイントです。
| 書き方 | 性質 | 使いどころ |
|---|---|---|
.+ | 貪欲:最長マッチ | 行全体を取りたいとき |
.+? | 非貪欲:最短マッチ | 短い単位でマッチしたいとき |
[^X]+ | 指定文字以外:確実に止まる | 文をまたぎたくないとき(最も安全) |
記号一覧まとめ
| 記号 | 意味 | 中国語での使用例 |
|---|---|---|
. | 任意の1文字 | 结.婚 離合詞の間に1文字 |
* | 0回以上の繰り返し | 结.*婚 間に何文字でも |
+ | 1回以上の繰り返し | 因为.+所以 呼応形式 |
? | 0回または1回 | 结.?婚 間に0〜1文字 |
{n,m} | n〜m回 | 是.{4,8}的 是…的構文 |
[] | ブラケット内のどれか1文字 | [啊吧呢] 語気助詞 |
[^] | ブラケット内以外の1文字 | [^,。?!] 句読点以外 |
^ | 行頭 | ^" セリフ行の抽出 |
$ | 行末 | 。$ 句点で終わる行 |
| | AまたはB | 但是|可是|不过 |
() | グループ・キャプチャ | (很|非常)大 |
\1(検索) | 後方参照 | (.)\1 畳語AA型 |
$1(置換) | 後方参照 | $1\n グループ後に改行 |
+? | 非貪欲マッチ | 是.+?的 |
\n | 改行 | 置換で改行を挿入 |
\t | タブ | タブ区切り処理 |
学.生[一二三四五六七八九十]{1,2}个^不吗。$(.)\1[^""]+
- 「打」と「电话」の間に0〜3文字が入るパターン
- 行末が「吗?」か「呢?」か「吧?」で終わる行
- 「虽然」で始まり「但是」か「可是」を含む行
- 「不」が2回以上連続するパターン
- 「我」か「你」か「他」のあとに「们」が来てもこなくてもよい
テキスト:今天天气很好。我们出去玩吧!你觉得怎么样?
句読点の後に改行を入れて1文1行にしてください。
打.{0,3}电话 2. [吗呢吧]?$ 3. ^虽然[^。!?]+(但是|可是) 4. 不{2,} 5. [我你他]们?
([。!?]) 置換:$1\n
① 探したいものを日本語で言語化する
② パターンの「骨格」(固定部分と可変部分)を決める
③ 可変部分を記号で表す(何文字か・句読点をまたぐか)
④ 過剰マッチ・漏れをチェックして修正する
4.1 離合詞を探す
「结婚」「睡觉」「生气」のように、分離して使われることがある動詞を離合詞といいます。
| 検索式 | 意味 | マッチ例 | 非マッチ例 |
|---|---|---|---|
结婚 | 間に何も入らない | 结婚 | 结了婚 |
结.婚 | ちょうど1文字 | 结了婚、结过婚 | 结婚、结了两次婚 |
结.?婚 | 0〜1文字 | 结婚、结了婚 | 结了两次婚 |
结.{1,3}婚 | 1〜3文字 | 结了婚、结不了婚 | 结婚 |
结[^,。?!]*婚 | 句読点をまたがない(最安全) | 同上(句点で確実に止まる) | — |
次の離合詞について「間に1〜3文字が入るもの」の検索式を書いてください。
- 生气
- 睡觉
- 洗澡
- 帮忙
- 离婚
- 请客
- 毕业
生.{1,3}气 2.睡.{1,3}觉 3.洗.{1,3}澡 4.帮.{1,3}忙 5.离.{1,3}婚 6.请.{1,3}客 7.毕.{1,3}业
4.2 呼応形式・不連続成分を探す
| 呼応形式 | 検索式 |
|---|---|
| 因为…所以 | 因为[^。!?]+所以 |
| 虽然…但是/可是/不过 | 虽然[^。!?]+(但是|可是|不过|却) |
| 越…越… | 越[^,。!?]+越 |
| 只要…就… | 只要[^,。!?]+就 |
| 只有…才… | 只有[^,。!?]+才 |
| 连…都/也… | 连[^,。!?]+(都|也) |
| 不是…而是… | 不是[^,。!?]+而是 |
| 既然…就… | 既然[^。!?]+就 |
| 一…就… | 一[^,。!?]+就 |
| 又…又… | 又[^,。!?]+又 |
4.3 「是…的」構文を探す
4.4 重ね型・畳語を探す
| 重ね型 | 例 | 検索式 |
|---|---|---|
| AA型 | 看看、好好、天天 | (.)\1 |
| ABB型 | 白胖胖、慢吞吞 | .(.)\ 1 |
| ABAB型 | 介绍介绍、高兴高兴 | (.{2})\1 |
| AABB型 | 热热闹闹、高高兴兴 | (.)\1(.)\2 |
| A一A型 | 看一看、写一写 | (.)一\1 |
| A了A型 | 看了看、想了想 | (.)了\1 |
| A不A型 | 是不是、来不来 | (.+)不\1 |
| AB不AB型 | 可以不可以、同意不同意 | (.{2})不\1 |
| AA[了的地]型 | 谢谢了、红红的、狠狠地 | (.)\1[了的地] |
4.5 量詞・数量表現を探す
# 漢数字+量詞 [一二三四五六七八九十百千两]+[个只条张本块件套] # 数字(漢字・算用字)+量詞(どちらも) ([一二三四五六七八九十百千两]+|[0-9]+)[个只条张本块件套] # 時間表現 ([一二三四五六七八九十]+|[0-9]+)(个)?(小时|分钟|秒)
4.6 語気助詞・文末表現を探す
| 目的 | 検索式 |
|---|---|
| 語気助詞(文末) | [啊吧呢嘛哦喽][,。?!] |
| 疑問文(吗) | 吗[?。]$ |
| セリフ行の抽出 | ^[""].+[""]$ |
| 台湾的語気助詞 | [喔欸耶哩啦][!。?,] |
| 感嘆文 | [啊哦哇][!]$ |
タイムコード・ハッシュタグ・発話者情報は、研究目的によっては重要な情報です。安易に削除してはいけません。
5.1 どんなデータが使えるか
| サイト・データ種別 | エンコード | 形式 | 主な用途 |
|---|---|---|---|
| 晋江文学城(小説) | GB18030 | HTML | 現代口語・セリフ・文体研究 |
| Weibo・SNS | UTF-8 | テキスト/API | 若者ことば・感情表現・SNS語 |
| 字幕ファイル(.srt) | UTF-8/GB | srt | ドラマ・映画のセリフ |
| HSK語彙・例文 | UTF-8 | HTML/PDF | 教材研究・語彙調査 |
| 人民日報・ニュース | UTF-8 | HTML | 書き言葉・ニュース文体 |
| ctext.org(古典) | UTF-8 | HTML | 古典中国語研究 |
5.2 晋江文学城(小説データ)
整形手順
<script[\s\S]+?</script>→ 空欄(scriptタグ削除)<style[\s\S]+?</style>→ 空欄(styleタグ削除)- 本文div(
id="paragraph_comment_content")より前後を手動削除 <[^>]+>→ 空欄(HTMLタグ削除) → スペース(エンティティ処理)+→ 空欄(全角スペース削除)^\n→ 空欄(空行削除)([。!?][""]?)→$1\n(1文1行化)
File → Save with Encoding → UTF-8 で保存し直してから作業してください。こんな研究ができる
5.3 字幕ファイル(srt)の処理
1 ← 字幕番号 00:00:01,000 --> 00:00:03,500 ← タイムコード(開始→終了) [王老师] 大家好,我们开始上课。 ← 発話者ラベル+本文 ← 空行(ブロック区切り) 2 00:00:05,200 --> 00:00:07,600 [学生甲] 老师好!
| 情報 | 語彙・文法研究 | 談話・会話分析 | 発話速度研究 |
|---|---|---|---|
| タイムコード | 削除してよい | 保持する | 保持する |
| 発話者情報 | 削除してよい | 保持する | 不要 |
| 空行(区切り) | 削除してよい | 保持する | 保持する |
語彙・文法研究用(タイムコード削除)
# 1. 行番号を削除 検索:^\d+$ # 2. タイムコードを削除 検索:^\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}$ # 3. 発話者ラベルを削除 検索:^\[[^\]]+\]\s* # 4. 空行を削除 検索:^\n # 5. 発話者をタブ区切りで残したい場合 検索:^\[([^\]]+)\]\s*(.+)$ 置換:$1\t$2 # → 「王老师\t大家好,我们开始上课。」形式でExcel集計可能
こんな研究ができる
5.4 SNS・Weiboデータの処理
@小李子2024 今天天气真好!#北京生活#想出去逛逛[微笑]。 昨天去了故宫[心动],超级漂亮!@王芳 你也去了吗? https://weibo.com/12345/abc #旅游#[doge] 转发 @旅游达人:北京的秋天是最美的季节。
・語彙・文法研究 → @・URL・ハッシュタグは削除してよい
・感情分析 →
[微笑][doge] 等の絵文字は感情の主要な手がかり(削除してはいけない)・話題・コミュニティ研究 → ハッシュタグは重要な情報(削除してはいけない)
語彙・文法研究用の整形(推奨順序)
# 1. ハッシュタグを削除(Weibo形式 #話題名#) 検索:#[^#\s]+# # 2. URLを削除 検索:https?://\S+ # 3. @ユーザー名を削除 検索:@[^\s,。!?]+ # 4. ブラケット絵文字を削除 検索:\[[^\]]+\] # 5. リツイート行を削除 検索:^(转发|RT)\s*@[^\s::]+[::]?\s* # 6. 空行を削除 検索:^\n
こんな研究ができる
5.5 フォルダ構成の設計
YYYYMMDD 形式。連番は 01 02(ゼロ埋め)にするとソートしやすい。5.6 Pythonで自動化する
ファイルが数十〜数百になると手作業には限界があります。Pythonを使えば整形作業を一括自動化できます。
HTML本文の一括抽出(晋江の例)
import re, os, glob def extract_jinjian(html): # scriptタグ・styleタグを削除 html = re.sub(r'<script[\s\S]+?</script>', '', html) html = re.sub(r'<style[\s\S]+?</style>', '', html) # 本文divを抽出 m = re.search( r'id="paragraph_comment_content"[^>]*>([\s\S]+?)</div>\s*<div[^>]+id="favoriteshow', html) if not m: return None text = m.group(1) text = re.sub(r'<[^>]+>', '', text) # タグ削除 text = text.replace(' ', ' ') # エンティティ処理 text = re.sub(r' +', '', text) # 全角スペース削除 text = re.sub(r'\n{2,}', '\n', text) # 連続改行を整理 return text.strip() # フォルダ内のHTMLをまとめて処理 os.makedirs('text_files', exist_ok=True) for path in glob.glob('html_files/*.html'): basename = os.path.splitext(os.path.basename(path))[0] with open(path, encoding='gb18030', errors='replace') as f: text = extract_jinjian(f.read()) if text: with open(f'text_files/{basename}.txt', 'w', encoding='utf-8') as f: f.write(text) print(f'✓ {basename}.txt')
srtの一括整形
def clean_srt(srt, remove_speaker=True): text = re.sub(r'^\d+$', '', srt, flags=re.MULTILINE) text = re.sub(r'^\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}$', '', text, flags=re.MULTILINE) if remove_speaker: text = re.sub(r'^\[[^\]]+\]\s*', '', text, flags=re.MULTILINE) return re.sub(r'\n{2,}', '\n', text).strip()
5.7 こんな研究ができる
| データ | 研究テーマ例 | 主な検索式 |
|---|---|---|
| 晋江小説(複数作品) | 作家別の重ね型使用傾向の比較 | (.)\1 (.)\1(.)\2 |
| 大陸vs台湾ドラマ字幕 | 語気助詞の地域差 | [啊吧呢喔欸耶][,。?!] |
| 中国語教材(複数) | 離合詞のカバレッジ調査 | 结.{0,3}婚 等 |
| Weibo投稿(年代別) | 若者ことばの変化追跡 | 語彙リストとのマッチング |
| 近代教科書+現代小説 | 是…的構文の通時的変化 | 是[^,。]{2,10}的 |
| 複数ドラマ字幕 | 発話速度の作品間比較 | タイムコード+Python計算 |
6.1 Grep検索の使い方
File → Add Project Folderでデータフォルダを開くCtrl+Shift+F(Mac: Cmd+Shift+F)でプロジェクト検索を開く.*ボタンで正規表現モードをオン- 検索式を入力 → Enter
- 結果はファイル名・行番号・マッチした行で表示される
grep_folder 関数(第5章)を使うと便利です。6.2 実践演習課題
(.)\1)・AABB型((.)\1(.)\2)の頻度を比較し、作家による重ね型の使い方の違いを考察してください。.(.)\ 1)にヒットした例を集め、語種(形容詞・副詞・擬音語)を分類してください。。$ / [!?]$ / [啊吧呢]。$)の比率を比較してください。以下のステップで自分のテーマを研究してください。
- 研究テーマを1文で書く(例:「台湾ドラマと大陸ドラマの〇〇を比較する」)
- 必要なデータを収集・整形する(第5章)
- 検索式を設計する(第4章の思考ステップを使う)
- Grep検索で用例を収集する
- 結果を表やグラフにまとめる
- 「なぜそうなっているか」を考察する
7.1 タグ付けとは何か
通常のテキストに品詞・文法情報などのタグ(ラベル)を付加したものをタグ付きコーパス(annotated corpus)といいます。タグ付きにすることで「名詞のあとに動詞が来るパターン」のような、品詞レベルの検索が可能になります。
| 形式 | 例 | 特徴 |
|---|---|---|
| XML形式 | <v>结</v><asp>了</asp><v>婚</v> | 視覚的・構造的だが正規表現で扱いにくい |
| TSV形式(本書推奨) | 结\tv\n了\tasp\n婚\tv | 正規表現・Python・Excelで扱いやすい |
7.2 jiebaでTSV形式に自動タグ付け
jieba(结巴)は中国語の分かち書き・品詞付与ライブラリです。
# インストール pip install jieba import jieba.posseg as pseg text = "周锦渊待过最大的集体就是道观,他连连喊冤。" # 品詞付与してTSV形式で出力 with open('output.tsv', 'w', encoding='utf-8') as f: for word, pos in pseg.cut(text): f.write(f'{word}\t{pos}\n') # 出力例: # 周锦渊 nr (人名) # 待过 v (動詞) # 最大 a (形容詞) # 的 uj (助詞) # 集体 n (名詞)
jiebaの主要品詞タグ
| タグ | 品詞 | 例 |
|---|---|---|
n | 名詞 | 集体、道观、医生 |
v | 動詞 | 看、去、结婚 |
a | 形容詞 | 大、好、漂亮 |
d | 副詞 | 很、都、也 |
p | 前置詞 | 在、从、对 |
r | 代詞 | 他、我、这 |
m | 数詞 | 一、两、三 |
q | 量詞 | 个、次、本 |
nr | 人名 | 周锦渊、王芳 |
ns | 地名 | 北京、上海 |
jieba.load_userdict())を活用すると精度が上がります。7.3 TSVタグ付きデータの検索
TSV形式での正規表現検索
# TSVファイルの例(1行1形態素) 周锦渊 nr 待过 v 最大 a 的 uj 集体 n 就是 d # 「名詞のあとに動詞が来るパターン」を検索 # 検索式(Pulsarで): ^\S+\tn\n\S+\tv # → 「名詞(n)の次の行が動詞(v)」にマッチ # 「的」のあとに名詞が来るパターン ^的\tuj\n(\S+)\tn # → 「的+名詞」の名詞を抽出できる
Pythonでの活用例
import re # TSVファイルを読み込んで動詞リストを抽出 with open('output.tsv', encoding='utf-8') as f: lines = f.readlines() verbs = [line.split('\t')[0] for line in lines if line.strip().endswith('\tv')] print("動詞一覧:", verbs[:10]) # 動詞の頻度集計 from collections import Counter verb_freq = Counter(verbs) for word, count in verb_freq.most_common(10): print(f'{word}: {count}回')
- 沈国威・氷野善寛(2005)「Windowsパソコンにおける中国語の検索——EmEditorを例に」関西大学東西学術研究所紀要
- 沈国威(2000)『電脳による中国語研究のススメ』白帝社
ゼロから学ぶ正規表現による中国語テキスト検索・分析入門