藏语自然语言处理入门 - 1 清理文本
本课目标
- 把看起来一样、编码不一的字符统一。
- 把不需要的内容清掉:页码、括号注释、装饰符号、多余空格。
- 按藏文的句末符号 ། / ༎ 把文本切成句子,一行一句。
做完你会得到两个文件:
cleaned.txt
(干净版本)sentences.txt
(每行一句,后面要用来分词/找相似句)
0. 开始前(2分钟)
用本地 Python 或 Google Colab 都行。先装一个库:
pip install regex
为啥装这个?它比内置的
re
更友好地处理 Unicode。
1. 准备一段小文本(2分钟)
新建 raw.txt
(UTF-8)。先放几行示例,等会儿你可以换成自己的课本内容:
༄༅། །
བོད་ཡིག་ནི་ སྐད་ཡིག་ཅིག་ཡིན། 1
འདི་ནས་ སློབ་ཚན་གསར་པ་འགོ་འཛུགས། 2
(དཔེ་མཚོན།) སློབ་ཕྲུག་ཚོས་ ཚིག་གྲུབ་སྦྱོང་།
你会看到:前面有装饰,行尾有“1/2”,还有括号注释和乱空格——这些就是我们要清理的内容。
2. 一段“万能清洗器”(直接可用,5分钟)
把下面代码粘到 clean.py
(或 Colab 单元格)。不用改参数,直接用。
···
# clean_tibetan.py
# 用法:
# 1) pip install regex
# 2) 准备 raw.txt(UTF-8)
# 3) python clean_tibetan.py
# 产出:cleaned.txt(干净文本)/ sentences.txt(一行一句)/ stats.txt(简单统计)import unicodedata
import regex as re
from pathlib import Path# ========== 1) 统一编码 ==========
def normalize_nfc(text: str) -> str:return unicodedata.normalize("NFC", text)# ========== 2) 去噪:页码/括注/装饰 ==========
def denoise(text: str) -> str:# 行尾纯数字(常见页码)text = re.sub(r"[ \t]*\b\d+\b[ \t]*$", "", text, flags=re.M)# 括号内的短注释(如不想删除,注释掉下一行)text = re.sub(r"\([^\n)]{1,30}\)", "", text)# 行首装饰符(༄༅ 等)text = re.sub(r"^[ \t]*[༄༅]+[།]*\s*", "", text, flags=re.M)return text# ========== 3) 空白收拾:空格/空行 ==========
def tidy_spaces(text: str) -> str:text = re.sub(r"[ \t]+", " ", text) # 多空格 → 单空格text = re.sub(r"\n{3,}", "\n\n", text) # >2 连续空行 → 2 个text = re.sub(r"[ \t]+\n", "\n", text) # 行尾空格return text.strip()# ========== 4) 仅保留藏文 + 常用标点(可选)==========
def keep_tibetan_only(text: str, keep_ascii_digits: bool=False) -> str:keep = {"་","།","༎"," ","\n"}out = []for ch in text:code = ord(ch)if 0x0F00 <= code <= 0x0FFF or ch in keep:out.append(ch)elif keep_ascii_digits and (ch.isascii() and (ch.isalpha() or ch.isdigit() or ch in "-_/.")):out.append(ch)return "".join(out)# ========== 5) 删除只含句末符或空白的行(关键改进)==========
def drop_punct_only_lines(text: str) -> str:# 只包含 །/༎/空白 的整行 → 删除return re.sub(r"(?m)^[\s།༎]+$", "", text).strip()# ========== 6) 切句(按 །/༎)==========
def split_sentences(text: str) -> list[str]:mark = "<<<SPLIT>>>"tmp = re.sub(r"([།༎]+)", r"\1"+mark, text)parts = [p.strip() for p in tmp.split(mark)]return [p for p in parts if p]# ========== 7) 清洗管道 ==========
def clean_pipeline(raw_text: str, only_tibetan: bool=True, keep_ascii_digits: bool=False):t = normalize_nfc(raw_text)t = denoise(t)t = tidy_spaces(t)if only_tibetan:t = keep_tibetan_only(t, keep_ascii_digits=keep_ascii_digits)t = tidy_spaces(t)t = drop_punct_only_lines(t) # <- 新增关键步骤sents = split_sentences(t)return t, sents# ========== 8) I/O 与统计 ==========
def run(infile="raw.txt", out_clean="cleaned.txt", out_sents="sentences.txt", out_stat="stats.txt"):raw = Path(infile).read_text(encoding="utf-8")clean, sents = clean_pipeline(raw, only_tibetan=True, keep_ascii_digits=False)Path(out_clean).write_text(clean, encoding="utf-8")Path(out_sents).write_text("\n".join(sents), encoding="utf-8")# 简单统计lengths = [len(s) for s in sents]lines = [f"句子数: {len(sents)}",f"平均句长(字符): {round(sum(lengths)/max(1,len(lengths)), 2)}",f"最短/最长句长: {min(lengths) if lengths else 0} / {max(lengths) if lengths else 0}"]Path(out_stat).write_text("\n".join(lines), encoding="utf-8")print("✅ 已输出:", out_clean, "/", out_sents, "/", out_stat)print("👉 预览 cleaned.txt:", clean[:80], ("..." if len(clean) > 80 else ""))if __name__ == "__main__":run()
这段代码的“人话版”说明:
normalize_nfc
:像给字体排队,避免“同字不同码”。denoise
:把页码、括号注释、装饰符统统扫掉。tidy_spaces
:把“乱七八糟的空格和空行”收拾干净。keep_tibetan_only
:只留藏文和常用标点(་ ། ༎
)。需要英文/数字?在函数里放开就好。split_sentences
:看到།
或༎
就切一刀,一行一句,清清楚楚。
3. 跑起来!(3分钟)
- 把
raw.txt
放在同一目录; - 把代码最后的
# run()
改成run()
; - 执行。你会得到:
cleaned.txt
:干净、好读、好用;sentences.txt
:每行一句(句末保留།/༎
)。
效果大概像这样:
བོད་ཡིག་ནི་ སྐད་ཡིག་ཅིག་ཡིན།
འདི་ནས་ སློབ་ཚན་གསར་པ་འགོ་འཛུགས།
སློབ་ཕྲུག་ཚོས་ ཚིག་གྲུབ་སྦྱོང།
句子数: 3
平均句长(字符): 30.33
最短/最长句长: 28 / 32
4. 30秒自检清单
-
cleaned.txt
里还有“1、2、(……)”这类杂质吗? -
sentences.txt
是否一行一句?末尾有།/༎
吗? - 需要保留英文/数字吗?如果要,把
keep_tibetan_only
放宽条件就行。
5. 常见小坑(快速排雷)
- 乱码/方块字:编辑器装个支持藏文的字体(如 Noto Sans Tibetan)。
- 删过头了:括号那行比较“狠”,正文真需要括号内容就把那行注释掉。
- 切得太碎:先保证“不错切”。粗一点没关系,后面再细化。