diff --git a/data/dictionary/BUILD.bazel b/data/dictionary/BUILD.bazel index fc14ac683..d47bfc54c 100644 --- a/data/dictionary/BUILD.bazel +++ b/data/dictionary/BUILD.bazel @@ -185,10 +185,12 @@ cc_test( srcs = ["DictionaryVariantRevPhrasesTest.cpp"], data = [ "HKVariants.txt", + "HKVariantsRev.txt", "HKVariantsRevPhrases.txt", "STCharacters.txt", "STPhrases.txt", "TWVariants.txt", + "TWVariantsRev.txt", "TWVariantsRevPhrases.txt", ], deps = [ diff --git a/data/dictionary/DictionaryVariantRevPhrasesTest.cpp b/data/dictionary/DictionaryVariantRevPhrasesTest.cpp index c7fa5788b..fe56e42ea 100644 --- a/data/dictionary/DictionaryVariantRevPhrasesTest.cpp +++ b/data/dictionary/DictionaryVariantRevPhrasesTest.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -78,11 +79,40 @@ bool IsInSameMultiVariantSet( return false; } +std::unordered_map LoadPreferredValues( + const std::string& path) { + std::ifstream stream(path); + EXPECT_TRUE(stream.is_open()) << path; + + std::unordered_map preferredValues; + std::string line; + while (std::getline(stream, line)) { + if (!IsDictionaryEntry(line)) { + continue; + } + + const size_t tab = line.find('\t'); + if (tab == std::string::npos) { + ADD_FAILURE() << line; + continue; + } + const std::string key = line.substr(0, tab); + const std::vector values = SplitValues(line.substr(tab + 1)); + if (!values.empty()) { + preferredValues[key] = values.front(); + } + } + return preferredValues; +} + std::unordered_set LoadReverseExceptionCharacters( - const std::string& variantsPath, const std::string& stCharactersPath) { + const std::string& variantsPath, const std::string& variantsRevPath, + const std::string& stCharactersPath) { const std::vector> variantSets = LoadMultiVariantSets(stCharactersPath); EXPECT_FALSE(variantSets.empty()); + const std::unordered_map reversePreferredValues = + LoadPreferredValues(variantsRevPath); std::ifstream stream(variantsPath); EXPECT_TRUE(stream.is_open()) << variantsPath; @@ -105,6 +135,11 @@ std::unordered_set LoadReverseExceptionCharacters( if (key == value) { continue; } + const auto reversePreferredValue = reversePreferredValues.find(value); + if (reversePreferredValue != reversePreferredValues.end() && + reversePreferredValue->second == value) { + continue; + } if (IsInSameMultiVariantSet(variantSets, key, value)) { exceptionChars.insert(value); } @@ -191,6 +226,8 @@ void ExpectRevPhrasesComplete(const std::string& variantsName) { const std::string revPhrasesName = variantsName + "RevPhrases.txt"; const std::string variantsPath = runfiles->Rlocation("_main/data/dictionary/" + variantsName + ".txt"); + const std::string variantsRevPath = + runfiles->Rlocation("_main/data/dictionary/" + variantsName + "Rev.txt"); const std::string phrasesPath = runfiles->Rlocation( "_main/data/dictionary/" + revPhrasesName); const std::string stCharactersPath = @@ -199,7 +236,8 @@ void ExpectRevPhrasesComplete(const std::string& variantsName) { runfiles->Rlocation("_main/data/dictionary/STPhrases.txt"); const std::unordered_set exceptionChars = - LoadReverseExceptionCharacters(variantsPath, stCharactersPath); + LoadReverseExceptionCharacters(variantsPath, variantsRevPath, + stCharactersPath); ASSERT_FALSE(exceptionChars.empty()) << variantsName; const std::unordered_set expected = diff --git a/data/dictionary/HKVariants.txt b/data/dictionary/HKVariants.txt index f8b3ee5c4..05aab586a 100644 --- a/data/dictionary/HKVariants.txt +++ b/data/dictionary/HKVariants.txt @@ -4,6 +4,8 @@ # License: Apache-2.0 (see LICENSE) # Source: https://github.com/ByVoid/OpenCC # Used in configs: s2hk.json, t2hk.json +# Also used to generate HKVariantsRev.txt. See data/scripts/reverse.py for +# the reverse dictionary generation algorithm. 僞 偽 兌 兑 @@ -18,11 +20,15 @@ 悅 悦 慍 愠 戶 户 +# @reverse-prefer: 才 +才 才 挩 捝 搵 揾 敓 敚 敘 敍 敘 柺 枴 +# @reverse-prefer: 核 +核 核 梲 棁 棱 稜 棱 榲 榅 @@ -33,6 +39,8 @@ 溼 濕 潙 溈 潨 潀 +# @reverse-prefer: 煙 +煙 煙 熅 煴 爲 為 癡 痴 @@ -43,13 +51,13 @@ 糉 粽 糉 糭 線 線 綫 縕 緼 -纔 才 +纔 才 纔 脣 唇 脫 脱 膃 腽 臥 卧 臺 台 -菸 煙 +菸 煙 菸 蒕 蒀 蔥 葱 蔿 蒍 @@ -57,7 +65,7 @@ 蛻 蜕 衆 眾 衛 衞 -覈 核 +覈 核 覈 說 説 踊 踴 轀 輼 diff --git a/data/dictionary/HKVariantsRevPhrases.txt b/data/dictionary/HKVariantsRevPhrases.txt index 6cdf6f693..2e525678c 100644 --- a/data/dictionary/HKVariantsRevPhrases.txt +++ b/data/dictionary/HKVariantsRevPhrases.txt @@ -19,119 +19,28 @@ 一家五口 一家五口 一家六口 一家六口 一家四口 一家四口 -一展長才 一展長才 -一流人才 一流人才 -一溜煙 一溜煙 -一表人才 一表人才 -一道煙 一道煙 一針 一針 一針見血 一針見血 -七孔生煙 七孔生煙 -七步之才 七步之才 -七步奇才 七步奇才 -七竅冒煙 七竅冒煙 -七竅生煙 七竅生煙 -三才 三才 -三才圖會 三才圖會 三針 三針 -下才 下才 -不喫煙火食 不喫煙火食 -不成才 不成才 -不才 不才 -不打不成才 不打不成才 -不良才 不良才 -不食人間煙火 不食人間煙火 -不食煙火 不食煙火 丟巧針 丟巧針 -中才 中才 -中核 中核 丹稜 丹稜 -之核 之核 九針 九針 -乾冒煙 乾冒煙 -乾奴才 乾奴才 亂針繡 亂針繡 -二流人才 二流人才 -于臺煙 于臺煙 -亞核 亞核 -人才 人才 -人才出衆 人才出衆 -人才外流 人才外流 -人才庫 人才庫 -人才流失 人才流失 -人才濟濟 人才濟濟 -人才輩出 人才輩出 -人才難得 人才難得 -人煙 人煙 -人煙浩穰 人煙浩穰 -人煙湊集 人煙湊集 -人煙稀少 人煙稀少 -人煙稠密 人煙稠密 -人煙輻輳 人煙輻輳 -人盡其才 人盡其才 -人間煙火 人間煙火 仙台 仙台 -仙才 仙才 -伊核 伊核 -作育英才 作育英才 -佳人才子 佳人才子 -個核 個核 -倒了核桃車子 倒了核桃車子 倒扣針兒 倒扣針兒 -偏才 偏才 做針線 做針線 -傲世輕才 傲世輕才 -儲訓人才 儲訓人才 -光煙霧 光煙霧 -內核 內核 -全才 全才 -全面禁止核試驗條約 全面禁止核試驗條約 八字方針 八字方針 -八斗之才 八斗之才 -八斗才 八斗才 -公才公望 公才公望 -六才子書 六才子書 -其核 其核 -冒煙 冒煙 -冒煙突火 冒煙突火 -冠世之才 冠世之才 -冰核 冰核 -凌煙閣 凌煙閣 -几案之才 几案之才 -凡才 凡才 -出倫之才 出倫之才 刀割針扎 刀割針扎 分針 分針 -初露才華 初露才華 別針 別針 刺胳針 刺胳針 刺針 刺針 -剋核 剋核 -前核 前核 -力薄才疏 力薄才疏 北港島綫 北港島線 -匡濟之才 匡濟之才 十針 十針 -千噸級核武器 千噸級核武器 南港島綫 南港島線 南針 南針 -博學多才 博學多才 -印核 印核 -卵核 卵核 -原子核 原子核 -原核 原核 -去核 去核 反時針 反時針 -反核 反核 -反煙 反煙 -反煙運動 反煙運動 -取才 取才 口吃 口吃 -口才 口才 -口才好 口才好 -口才辨給 口才辨給 -另起煙爨 另起煙爨 -可憎才 可憎才 台山 台山 台山市 台山市 台州 台州 @@ -143,106 +52,29 @@ 吃吃 喫喫 吃吃 吃子 喫子 吃子 向風針 向風針 -吞煙 吞煙 -含煙籠霧 含煙籠霧 -吸油煙機 吸油煙機 -呂芳煙 呂芳煙 -命世之才 命世之才 -命世才 命世才 -和核 和核 -唐才常 唐才常 唱針 唱針 啄針兒 啄針兒 -善才 善才 -善才童子 善才童子 -喝風呵煙 喝風呵煙 -喝風痾煙 喝風痾煙 -喫敲才 喫敲才 -喫煙 喫煙 -喬才 喬才 -單核 單核 -單核細胞增多症 單核細胞增多症 嗎啡針 嗎啡針 -嚴禁煙火 嚴禁煙火 -四才子 四才子 -四核 四核 -地核 地核 -地面核爆炸 地面核爆炸 -埋沒人才 埋沒人才 -堅臥煙霞 堅臥煙霞 -夕煙 夕煙 -外才 外才 -外核 外核 -多事逞才 多事逞才 -多才 多才 -多才多藝 多才多藝 -多核 多核 -大才 大才 -大才小用 大才小用 -大才槃槃 大才槃槃 大政方針 大政方針 -大曆十才子 大曆十才子 -大核 大核 大海撈針 大海撈針 -大煙 大煙 大頭針 大頭針 -大麻煙 大麻煙 天台 天台 天台女 天台女 天台宗 天台宗 天台山 天台山 天台縣 天台縣 -天妒英才 天妒英才 -天才 天才 -天才兒童 天才兒童 -天才出自勤奮 天才出自勤奮 -天才型 天才型 -天才教育 天才教育 -天才橫溢 天才橫溢 -天才論 天才論 -天縱之才 天縱之才 太乙神針 太乙神針 奇台 奇台 -奇才 奇才 -奇才異能 奇才異能 女人心海底針 女人心海底針 -女秀才 女秀才 -女貌郎才 女貌郎才 -奴才 奴才 -如墮煙霧 如墮煙霧 -妙才 妙才 -學優才贍 學優才贍 -學淺才疏 學淺才疏 -學疏才淺 學疏才淺 -宏內核 宏內核 定南針 定南針 定風針 定風針 -實才 實才 -將才 將才 將軍澳綫 將軍澳線 -將遇良才 將遇良才 -專才 專才 -專業人才 專業人才 -專門人才 專門人才 對針 對針 -小才大用 小才大用 -小才子 小才子 -小秀才 小秀才 -小秀才學堂 小秀才學堂 小針 小針 小針美容 小針美容 -少年才俊 少年才俊 -尺二秀才 尺二秀才 -屈才 屈才 -展才 展才 屯馬綫 屯馬線 -嵐煙波影 嵐煙波影 平針縫 平針縫 -幹才 幹才 幾針 幾針 -庸才 庸才 -廖本煙 廖本煙 -廩膳秀才 廩膳秀才 引線穿針 引線穿針 張口 張口 張柏芝 張柏芝 @@ -251,572 +83,89 @@ 強心針 強心針 弼針 弼針 彈針 彈針 -彗核 彗核 -往事如煙 往事如煙 -微核 微核 -徵才 徵才 -德才 德才 -德才兼備 德才兼備 -德薄才疏 德薄才疏 -志大才疏 志大才疏 -志大才短 志大才短 -志廣才疏 志廣才疏 -恃才傲物 恃才傲物 -恃才矜己 恃才矜己 -恃才自專 恃才自專 -意廣才疏 意廣才疏 -愛才 愛才 -愛才好士 愛才好士 -愛才如命 愛才如命 -愛才若渴 愛才若渴 -憐才 憐才 -懷才不遇 懷才不遇 -懷才抱德 懷才抱德 懸針 懸針 懸針垂露 懸針垂露 -成兆才 成兆才 -成核 成核 -戰術核武器 戰術核武器 手腕式指北針 手腕式指北針 -才人 才人 -才俊 才俊 -才儲八斗 才儲八斗 -才具 才具 -才兼文武 才兼文武 -才分 才分 -才力 才力 -才勇兼優 才勇兼優 -才名 才名 -才器 才器 -才士 才士 -才大難用 才大難用 -才女 才女 -才如史遷 才如史遷 -才媛 才媛 -才子 才子 -才子佳人 才子佳人 -才子書 才子書 -才學 才學 -才學兼優 才學兼優 -才守 才守 -才定 才定 -才幹 才幹 -才廣妨身 才廣妨身 -才微智淺 才微智淺 -才德 才德 -才德兼備 才德兼備 -才思 才思 -才思敏捷 才思敏捷 -才悟 才悟 -才情 才情 -才智 才智 -才望 才望 -才氣 才氣 -才氣無雙 才氣無雙 -才氣縱橫 才氣縱橫 -才氣過人 才氣過人 -才爲世出 才爲世出 -才用 才用 -才略 才略 -才略過人 才略過人 -才當曹斗 才當曹斗 -才疏學淺 才疏學淺 -才疏德薄 才疏德薄 -才疏志大 才疏志大 -才疏意廣 才疏意廣 -才疏計拙 才疏計拙 -才短氣粗 才短氣粗 -才秀人微 才秀人微 -才能 才能 -才能幹濟 才能幹濟 -才色 才色 -才華 才華 -才華出衆 才華出衆 -才華橫溢 才華橫溢 -才華洋溢 才華洋溢 -才華蓋世 才華蓋世 -才蔽識淺 才蔽識淺 -才藝 才藝 -才藝卓絕 才藝卓絕 -才藝技能 才藝技能 -才藝班 才藝班 -才藝秀 才藝秀 -才藻 才藻 -才語 才語 -才識 才識 -才識過人 才識過人 -才貌 才貌 -才貌出衆 才貌出衆 -才貌雙全 才貌雙全 -才貫二酉 才貫二酉 -才資 才資 -才輕德薄 才輕德薄 -才過子建 才過子建 -才過屈宋 才過屈宋 -才非玉潤 才非玉潤 -才高八斗 才高八斗 -才高意廣 才高意廣 -才高氣傲 才高氣傲 -才高行厚 才高行厚 -才高行潔 才高行潔 扎針 扎針 打完針 打完針 打針 打針 披針形葉 披針形葉 抵針 抵針 -抽油煙機 抽油煙機 拈針指 拈針指 指北針 指北針 指南針 指南針 指揮台 指揮台 指針 指針 指針式 指針式 -捲煙畫片 捲煙畫片 -捷才 捷才 -掃眉才子 掃眉才子 -排煙 排煙 探針 探針 -接紹香煙 接紹香煙 -接續香煙 接續香煙 控制台 控制台 插針 插針 -揚己露才 揚己露才 搖針 搖針 搗針 搗針 撞針 撞針 -撥亂之才 撥亂之才 -擬核 擬核 擺針 擺針 收針 收針 -放射性煙羽 放射性煙羽 -放煙 放煙 -放煙幕 放煙幕 -放煙幕彈 放煙幕彈 -放煙火 放煙火 教育方針 教育方針 敹一針 敹一針 -文才 文才 -文武全才 文武全才 -文選爛秀才半 文選爛秀才半 -斗筲之才 斗筲之才 -斷煙 斷煙 方針 方針 -施放煙火 施放煙火 時針 時針 -晶核 晶核 暈針 暈針 -暮煙 暮煙 -曠世之才 曠世之才 -曠世奇才 曠世奇才 -曠世逸才 曠世逸才 曲別針 曲別針 -月核 月核 -有才 有才 -有才幹 有才幹 -有才無命 有才無命 -有核 有核 -有煙煤 有煙煤 -朝核 朝核 -未易才 未易才 -朽木之才 朽木之才 -杏核 杏核 東九龍綫 東九龍線 東海撈針 東海撈針 東涌綫 東涌線 東鐵綫 東鐵線 -杳無人煙 杳無人煙 -松煙 松煙 -松煙墨 松煙墨 松針 松針 -板煙 板煙 -果核 果核 枝針 枝針 -柳煙花霧 柳煙花霧 -核下 核下 -核二廠 核二廠 -核人 核人 -核仁 核仁 -核以 核以 -核僵持 核僵持 -核兒 核兒 -核冬天 核冬天 -核出口控制 核出口控制 -核力 核力 -核化 核化 -核區 核區 -核可 核可 -核合成 核合成 -核和 核和 -核四 核四 -核型 核型 -核子 核子 -核子廠 核子廠 -核孔 核孔 -核島 核島 -核工 核工 -核彈 核彈 -核心 核心 -核戰 核戰 -核戰鬥部 核戰鬥部 -核技術 核技術 -核數 核數 -核是 核是 -核有 核有 -核果 核果 -核桃 核桃 -核武 核武 -核火箭發動機 核火箭發動機 -核炫 核炫 -核燃料後處理 核燃料後處理 -核爆 核爆 -核爆炸煙雲 核爆炸煙雲 -核狀 核狀 -核球 核球 -核甘 核甘 -核當量 核當量 -核發 核發 -核發電 核發電 -核發電廠 核發電廠 -核的 核的 -核磁 核磁 -核種 核種 -核突 核突 -核粒 核粒 -核糖 核糖 -核糖核酸 核糖核酸 -核素 核素 -核線 核線 -核能 核能 -核能技術 核能技術 -核能發電 核能發電 -核能發電廠 核能發電廠 -核能電廠 核能電廠 -核膜 核膜 -核苷 核苷 -核菌 核菌 -核融合 核融合 -核融合發電 核融合發電 -核解 核解 -核計劃 核計劃 -核試 核試 -核談 核談 -核質 核質 -核載 核載 -核辦 核辦 -核配 核配 -核酪 核酪 -核酶 核酶 -核酸 核酸 -核防禦 核防禦 -核電 核電 -核電廠 核電廠 -核電磁脈衝 核電磁脈衝 -核體 核體 -核點 核點 -桃核 桃核 -桃核雕 桃核雕 -桐花煙 桐花煙 桑針 桑針 -梅核 梅核 -棄核 棄核 -棋逢對手將遇良才 棋逢對手將遇良才 棒針 棒針 棒針衫 棒針衫 -棗核 棗核 棘針 棘針 棘針科 棘針科 棘針門 棘針門 -極核 極核 -槃才 槃才 -槃槃大才 槃槃大才 -橘核 橘核 機場快綫 機場快線 -檢核 檢核 -檢核表 檢核表 步線行針 步線行針 -步飛煙傳 步飛煙傳 -歪才 歪才 -歷練之才 歷練之才 -歸煙 歸煙 -殺才 殺才 毒針 毒針 -比才 比才 毛線針 毛線針 毫針 毫針 -氘核 氘核 水底撈針 水底撈針 -水煙 水煙 -求才 求才 -求才若渴 求才若渴 -江南四大才子 江南四大才子 -江淹才盡 江淹才盡 -江郎才盡 江郎才盡 -沒人煙 沒人煙 -沒有人煙 沒有人煙 沙中綫 沙中線 -油煙 油煙 -油煙機 油煙機 -沿才授職 沿才授職 注射針 注射針 注射針頭 注射針頭 洗面皂 洗面皂 洗髮皂 洗髮皂 -洛陽才子 洛陽才子 浙江天台縣 浙江天台縣 -浩如煙氣 浩如煙氣 -浩如煙海 浩如煙海 海底撈針 海底撈針 -深海煙囪 深海煙囪 -清水煙 清水煙 港島綫 港島線 -渺無人煙 渺無人煙 -滿腹才學 滿腹才學 漏針 漏針 -潑才 潑才 -潑煙花 潑煙花 -潑賤煙花 潑賤煙花 -濃煙 濃煙 -濟世之才 濟世之才 -火滅煙消 火滅煙消 -火煙 火煙 -灰飛煙滅 灰飛煙滅 -炊煙 炊煙 -炊煙裊裊 炊煙裊裊 炮台山循道衛理中學 炮台山循道衛理中學 -烏煙瘴氣 烏煙瘴氣 -烤煙 烤煙 -烽煙 烽煙 -烽煙四起 烽煙四起 -烽煙四起戰火紛飛 烽煙四起戰火紛飛 -烽煙遍地 烽煙遍地 -無核 無核 -無煙 無煙 -無煙囪工業 無煙囪工業 -無煙火藥 無煙火藥 -無煙炭 無煙炭 -無煙焦煤 無煙焦煤 -無煙煤 無煙煤 無線新聞台 無線新聞台 無針不引線 無針不引線 無針注射器 無針注射器 -煙供 煙供 -煙傢伙 煙傢伙 -煙具 煙具 -煙合包 煙合包 -煙味 煙味 -煙商 煙商 -煙囪 煙囪 -煙土 煙土 -煙塵 煙塵 -煙夜蛾 煙夜蛾 -煙子 煙子 -煙客 煙客 -煙岸 煙岸 -煙嵐 煙嵐 -煙嵐雲岫 煙嵐雲岫 -煙幕 煙幕 -煙幕彈 煙幕彈 -煙戶 煙戶 -煙景 煙景 -煙月 煙月 -煙月場 煙月場 -煙月牌 煙月牌 -煙柱 煙柱 -煙桿 煙桿 -煙槍 煙槍 -煙槍洞 煙槍洞 -煙樹 煙樹 -煙機 煙機 -煙毒 煙毒 -煙毒犯 煙毒犯 -煙氣 煙氣 -煙油 煙油 -煙泡 煙泡 -煙波 煙波 -煙波客 煙波客 -煙波浩渺 煙波浩渺 -煙波萬頃 煙波萬頃 -煙波釣叟 煙波釣叟 -煙波釣徒 煙波釣徒 -煙海 煙海 -煙消冰釋 煙消冰釋 -煙消火滅 煙消火滅 -煙消雲散 煙消雲散 -煙消霧散 煙消霧散 -煙渚 煙渚 -煙火 煙火 -煙火之警 煙火之警 -煙火氣 煙火氣 -煙火秀 煙火秀 -煙火節 煙火節 -煙火鄰居 煙火鄰居 -煙火食 煙火食 -煙煤 煙煤 -煙熅 煙熅 -煙熏火燎 煙熏火燎 -煙燄障天 煙燄障天 -煙燈 煙燈 -煙燻 煙燻 -煙燻妝 煙燻妝 -煙爨 煙爨 -煙生喉舌 煙生喉舌 -煙瘴 煙瘴 -煙癮 煙癮 -煙盒 煙盒 -煙硝 煙硝 -煙硝味 煙硝味 -煙穗 煙穗 -煙突 煙突 -煙窩 煙窩 -煙筒 煙筒 -煙筒山 煙筒山 -煙管 煙管 -煙管麪 煙管麪 -煙篷 煙篷 -煙籠霧鎖 煙籠霧鎖 -煙粉 煙粉 -煙縷 煙縷 -煙肉 煙肉 -煙膏 煙膏 -煙臭 煙臭 -煙臭味 煙臭味 -煙臺 煙臺 -煙臺地區 煙臺地區 -煙臺市 煙臺市 -煙臺師範學院 煙臺師範學院 -煙花 煙花 -煙花債 煙花債 -煙花場 煙花場 -煙花女 煙花女 -煙花寨 煙花寨 -煙花寨主 煙花寨主 -煙花巷 煙花巷 -煙花市 煙花市 -煙花廠 煙花廠 -煙花柳巷 煙花柳巷 -煙花窟 煙花窟 -煙花簿 煙花簿 -煙花粉柳 煙花粉柳 -煙花粉黛 煙花粉黛 -煙花行院 煙花行院 -煙花門戶 煙花門戶 -煙花陣 煙花陣 -煙花隊 煙花隊 -煙花風月 煙花風月 -煙苗 煙苗 -煙蓑雨笠 煙蓑雨笠 -煙視媚行 煙視媚行 -煙酒不沾 煙酒不沾 -煙酸 煙酸 -煙鍋 煙鍋 -煙雨 煙雨 -煙雨樓 煙雨樓 -煙雲 煙雲 -煙雲供養 煙雲供養 -煙雲過眼 煙雲過眼 -煙霏 煙霏 -煙霏霧集 煙霏霧集 -煙霏露結 煙霏露結 -煙霞 煙霞 -煙霞外人 煙霞外人 -煙霞痼疾 煙霞痼疾 -煙霞癖 煙霞癖 -煙霧 煙霧 -煙霧劑 煙霧劑 -煙霧彈 煙霧彈 -煙霧瀰漫 煙霧瀰漫 -煙霧症 煙霧症 -煙霧迷漫 煙霧迷漫 -煙霾 煙霾 -煙靄 煙靄 -煙飛星散 煙飛星散 -煙館 煙館 -煙鬟 煙鬟 -煙鬼 煙鬼 -煤核 煤核 -煤煙 煤煙 -熔核 熔核 -熱核 熱核 燔針 燔針 -片善小才 片善小才 -物色人才 物色人才 -特殊才能 特殊才能 -狗才 狗才 -狼煙 狼煙 -狼煙四起 狼煙四起 -玉尺量才 玉尺量才 -王佐之才 王佐之才 -瑣才 瑣才 -甄才品能 甄才品能 -甄選人才 甄選人才 -男才女貌 男才女貌 -畎畝下才 畎畝下才 留針 留針 -異才 異才 -當世才度 當世才度 -疑核 疑核 -痔核 痔核 -痛失英才 痛失英才 -登庸人才 登庸人才 -發展核武器 發展核武器 -發煙 發煙 -發煙劑 發煙劑 -白煙 白煙 -白鶴秀才 白鶴秀才 -百萬噸級核武器 百萬噸級核武器 -百里之才 百里之才 皂化 皂化 皂莢 皂莢 皂莢樹 皂莢樹 皂角 皂角 -的核 的核 -真才實學 真才實學 -真核 真核 -矜才使氣 矜才使氣 -矜能負才 矜能負才 短針 短針 石針 石針 -硝煙 硝煙 -硝煙彈雨 硝煙彈雨 -硬核 硬核 硬肥皂 硬肥皂 -碌碌庸才 碌碌庸才 -磁核 磁核 磁針 磁針 磨杵成針 磨杵成針 磨針溪 磨針溪 磨鐵成針 磨鐵成針 -社交才能 社交才能 -禁核 禁核 -秀才 秀才 -秀才不出門能知天下事 秀才不出門能知天下事 -秀才人情 秀才人情 -秀才作醫如菜作虀 秀才作醫如菜作虀 -秀才造反 秀才造反 秒針 秒針 秧針 秧針 穆稜 穆稜 穿針 穿針 穿針引線 穿針引線 穿針走線 穿針走線 -管理人才 管理人才 -篆煙 篆煙 -精核 精核 -約核 約核 紋光針 紋光針 -紛如煙 紛如煙 細針密縷 細針密縷 -結核 結核 -結核桿菌 結核桿菌 -絕才 絕才 絞包針 絞包針 給個棒錘當針認 給個棒錘當針認 綏稜 綏稜 -經世之才 經世之才 -經國之才 經國之才 -經濟之才 經濟之才 -綠煙紅霧 綠煙紅霧 -網羅人才 網羅人才 綿裏藏針 綿裏藏針 綿裏針 綿裏針 縫衣針 縫衣針 @@ -829,113 +178,44 @@ 繡花針兒 繡花針兒 繡針 繡針 羅盤針 羅盤針 -美才 美才 美白針 美白針 -老奴才 老奴才 -老煙槍 老煙槍 -老煙鬼 老煙鬼 -耐多藥結核病 耐多藥結核病 耳針 耳針 -聰明才智 聰明才智 肥皂 肥皂 肥皂劇 肥皂劇 肥皂泡 肥皂泡 肥皂粉 肥皂粉 肥皂絲 肥皂絲 肥皂莢 肥皂莢 -育才 育才 胃口 胃口 -胡才勇 胡才勇 胸針 胸針 -自學成才 自學成才 -自核 自核 臺灣台 臺灣台 -舊公煙 舊公煙 船不漏針漏針沒外人 船不漏針漏針沒外人 -船載的金銀填不滿煙花債 船載的金銀填不滿煙花債 花兒針 花兒針 -花煙館 花煙館 -英才 英才 -英才俊偉 英才俊偉 -茂才 茂才 -茂才異等 茂才異等 茅針 茅針 -茲核 茲核 荃灣綫 荃灣線 -荒無人煙 荒無人煙 -荒煙漫草 荒煙漫草 -荒煙蔓草 荒煙蔓草 -菌核 菌核 -菲才寡學 菲才寡學 葉針 葉針 -蓋世之才 蓋世之才 藏針縫 藏針縫 藥皂 藥皂 藥針 藥針 -蘋果核 蘋果核 -蘭花煙 蘭花煙 蛇口蜂針 蛇口蜂針 螫針 螫針 蠻針瞎灸 蠻針瞎灸 -行短才喬 行短才喬 -行短才高 行短才高 -裊裊炊煙 裊裊炊煙 補血針 補血針 補針 補針 見縫插針 見縫插針 觀塘綫 觀塘線 討針線 討針線 -詠雪之才 詠雪之才 -詩才 詩才 -誇才賣智 誇才賣智 -誰家竈內無煙 誰家竈內無煙 -謊敲才 謊敲才 -識多才廣 識多才廣 -識才 識才 -識才尊賢 識才尊賢 象牙針尖 象牙針尖 -負才 負才 -負才任氣 負才任氣 -負才使氣 負才使氣 賀爾蒙針 賀爾蒙針 -賢才 賢才 -賤才 賤才 -超世之才 超世之才 -趫才 趫才 跳針 跳針 蹇吃 蹇吃 -身才 身才 軟肥皂 軟肥皂 -輇才 輇才 -輕核 輕核 -輕煙 輕煙 -辯才 辯才 -辯才天 辯才天 -辯才無礙 辯才無礙 迪士尼綫 迪士尼線 迴紋針 迴紋針 退針 退針 逆時針 逆時針 -通人達才 通人達才 -通才 通才 -通才教育 通才教育 -通才練識 通才練識 -造就人才 造就人才 -逸才 逸才 -逸羣之才 逸羣之才 -過人才略 過人才略 -過眼煙雲 過眼煙雲 -過眼雲煙 過眼雲煙 -適才 適才 -選才 選才 -遺才 遺才 避雷針 避雷針 -邊核 邊核 -郎才女姿 郎才女姿 -郎才女貌 郎才女貌 鄧艾吃 鄧艾吃 -野無遺才 野無遺才 -量才錄用 量才錄用 金針 金針 金針山 金針山 金針度人 金針度人 @@ -1010,43 +290,17 @@ 針鼻兒 針鼻兒 釦針 釦針 鉤針 鉤針 -銀核 銀核 銀針 銀針 鋼針 鋼針 錶針 錶針 -鍋煙 鍋煙 -鍋煙子 鍋煙子 鐵針 鐵針 -鑑核備查 鑑核備查 -長才 長才 -長煙 長煙 -長煙一空 長煙一空 長針 長針 開口 開口 防疫針 防疫針 -陰核 陰核 -隨才器使 隨才器使 -雄才 雄才 -雄才大略 雄才大略 -雌核 雌核 -雙核 雙核 -雙鏈核酸 雙鏈核酸 -雪茄煙 雪茄煙 -雲煙 雲煙 -雲煙過眼 雲煙過眼 -雲煙過眼錄 雲煙過眼錄 電唱針 電唱針 電針 電針 電針麻醉 電針麻醉 -需才孔亟 需才孔亟 -露才 露才 -露才揚己 露才揚己 -霸才 霸才 -青煙 青煙 -非才 非才 -非核 非核 面皂 面皂 -頂核 頂核 頂針 頂針 頂針兒 頂針兒 頂針捱住 頂針捱住 @@ -1055,34 +309,14 @@ 預防針 預防針 領帶針 領帶針 風向針 風向針 -風流才子 風流才子 -風煙 風煙 -飛煙傳 飛煙傳 飛針走線 飛針走線 -飽學秀才 飽學秀才 -香煙不絕 香煙不絕 -香煙後代 香煙後代 香皂 香皂 -馮驥才 馮驥才 -驚才絕豔 驚才絕豔 骨針 骨針 -高才 高才 -高才生 高才生 -高級管理人才 高級管理人才 髮針 髮針 -鬼才 鬼才 鬼針草 鬼針草 鳳台 鳳台 -鴉片煙 鴉片煙 鹽水針 鹽水針 -麴秀才 麴秀才 麻醉針 麻醉針 黃成 黃成 -黃有才 黃有才 -黑煙 黑煙 -點染雲煙 點染雲煙 -點核 點核 鼻針療法 鼻針療法 -齊煙九點 齊煙九點 齧蘗吞針 齧蘗吞針 -龍煙鐵礦 龍煙鐵礦 diff --git a/data/dictionary/TWVariants.txt b/data/dictionary/TWVariants.txt index 09e4a1408..da724747d 100644 --- a/data/dictionary/TWVariants.txt +++ b/data/dictionary/TWVariants.txt @@ -4,6 +4,8 @@ # License: Apache-2.0 (see LICENSE) # Source: https://github.com/ByVoid/OpenCC # Used in configs: s2tw.json, s2twp.json, t2tw.json +# Also used to generate TWVariantsRev.txt. See data/scripts/reverse.py for +# the reverse dictionary generation algorithm. 僞 偽 啓 啟 diff --git a/data/scripts/common.py b/data/scripts/common.py index 28d04ee68..6dfea3ad9 100644 --- a/data/scripts/common.py +++ b/data/scripts/common.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +REVERSE_PREFERENCE_PREFIX = '# @reverse-prefer:' + class BaseDict: def _iter(self, file): with open(file, 'r', encoding='utf-8') as fh: @@ -36,6 +38,28 @@ def get_line(entry): class Dict(BaseDict): + def load(self, file): + self.reverse_preferences = {} + super().load(file) + + def iter(self, file): + for item in super().iter(file): + yield item + + def _iter(self, file): + for line_number, line in enumerate(super()._iter(file), start=1): + line_text = line.rstrip('\r\n') + if line_text.startswith(REVERSE_PREFERENCE_PREFIX): + if not hasattr(self, 'reverse_preferences'): + self.reverse_preferences = {} + fields = line_text[len(REVERSE_PREFERENCE_PREFIX):].split() + if len(fields) not in (1, 2): + raise ValueError( + f'Invalid reverse preference at line {line_number}: {line_text}' + ) + self.reverse_preferences[fields[0]] = fields[-1] + yield line + def sort(self): self.entries.sort(key=lambda e: e['key']) @@ -48,6 +72,13 @@ def swap(self): dic[value].append(key) else: dic[value] = [key] + for key, preferred_value in getattr(self, 'reverse_preferences', {}).items(): + if key not in dic or preferred_value not in dic[key]: + raise ValueError( + f'Reverse preference {key} -> {preferred_value} has no matching mapping' + ) + values = dic[key] + values.insert(0, values.pop(values.index(preferred_value))) self.entries = [{'key': key, 'values': values} for key, values in dic.items()] diff --git a/data/scripts/common_test.py b/data/scripts/common_test.py index 8f343a2e2..b6dbba281 100644 --- a/data/scripts/common_test.py +++ b/data/scripts/common_test.py @@ -86,6 +86,27 @@ def test_reverse_items(self): "x\tA B\ny\tA\n", ) + def test_reverse_items_applies_reverse_preference(self): + out = self.run_file_op( + reverse_items, + "# @reverse-prefer: x B\nA\tx\nB\tx\n", + ) + self.assertEqual(out.decode("utf-8"), "x\tB A\n") + + def test_reverse_items_uses_key_as_default_reverse_preference(self): + out = self.run_file_op( + reverse_items, + "# @reverse-prefer: x\nx\tx\nA\tx\n", + ) + self.assertEqual(out.decode("utf-8"), "x\tx A\n") + + def test_reverse_items_rejects_unmatched_reverse_preference(self): + with self.assertRaises(ValueError): + self.run_file_op( + reverse_items, + "# @reverse-prefer: x C\nA\tx\nB\tx\n", + ) + def test_find_target_items_matches_values_only(self): with tempfile.TemporaryDirectory() as tmpdir: src = os.path.join(tmpdir, "in.txt") diff --git a/test/testcases/testcases.json b/test/testcases/testcases.json index 891bc6d5f..93064ccf6 100755 --- a/test/testcases/testcases.json +++ b/test/testcases/testcases.json @@ -1093,9 +1093,16 @@ }, { "id": "BYVoid_OpenCC_Issue_1246_hk_variant_reverse_phrases", - "input": "才智 核心 方針 肥皂 香皂 接續香煙", + "input": "才智 核心 方針 肥皂 香皂 接續香煙 煙", "expected": { - "hk2t": "才智 核心 方針 肥皂 香皂 接續香煙" + "hk2t": "才智 核心 方針 肥皂 香皂 接續香煙 煙" + } + }, + { + "id": "hk_variants_rev_preferred_characters", + "input": "才 核 煙", + "expected": { + "hk2t": "才 核 煙" } }, { @@ -1121,7 +1128,7 @@ "id": "BYVoid_OpenCC_Issue_1246_test", "input": "這樣才行", "expected": { - "hk2t": "這樣纔行", + "hk2t": "這樣才行", "hk2s": "这样才行", "tw2t": "這樣纔行", "tw2s": "这样才行"