BYVoid / OpenCC

Conversion between Traditional and Simplified Chinese
https://opencc.byvoid.com/
Apache License 2.0
8.35k stars 974 forks source link

支援 Unicode 組合字 #429

Open danny0838 opened 4 years ago

danny0838 commented 4 years ago

所謂 Unicode 組合字是指多個字元表示一個「字」的情形,包括歐洲語言的 combining diacritical marks、漢字的 ideographic description characters、variation selectors 等等。

目前版本的 OpenCC 由於對 Unicode 組合字沒有特別支援,使用時會發生不可欲的後果,例如因為字典有「虫=>蟲」和「风=>風」的對應,導致簡轉繁時「⿰虫风」變成「⿰蟲風」的錯誤。合理的做法應該是把待轉文字及字典裡的組合字個別視為一個「字」,比如「⿰虫风」是一個字,「虫=>蟲」或「风=>蟲」不該對它做轉換,而「⿰虫风=>𧍯」則應該對它做轉換。

以下是用 Python 寫成的簡易組合字長度偵測算法,可做為分「字」的基礎,供參:

def composite_length(text, pos):
    """Get the length of the Unicode composite at pos.

    A unicode composite is a complex of characters with composers.
    For example, an ideographic description sequence (IDS),
    or a hanzi with a variant selector (VS), etc.
    """
    i = pos
    total = len(text)
    length = 1

    while length and i < total:
        code = ord(text[i])

        # check if the current char is a prefix composer
        if code == 0x303E:
            # ideographic variation indicator
            length += 1
        elif 0x2FF0 <= code <= 0x2FF1 or 0x2FF4 <= code <= 0x2FFB:
            # IDS binary operator
            length += 2
        elif 0x2FF2 <= code <= 0x2FF3:
            # IDS trinary operator
            length += 3

        # check if the next char is a postfix composer
        if i + 1 < total:
            code = ord(text[i + 1])
            if 0xFE00 <= code <= 0xFE0F:
                # variation selectors
                length += 1
            elif 0xE0100 <= code <= 0xE01E:
                # variation selectors supplement
                length += 1
            elif 0x180B <= code <= 0x180D:
                # Mongolian free variation selectors
                length += 1
            elif 0x0300 <= code <= 0x036F:
                # combining diacritical marks
                length += 1
            elif 0x1AB0 <= code <= 0x1AFF:
                # combining diacritical marks extended
                length += 1
            elif 0x1DC0 <= code <= 0x1DFF:
                # combining diacritical marks supplement
                length += 1
            elif 0x20D0 <= code <= 0x20FF:
                # combining diacritical marks for symbols
                length += 1
            elif 0xFE20 <= code <= 0xFE2F:
                # combining half marks
                length += 1

        i += 1
        length -= 1

    return i - pos
mingruimingrui commented 4 years ago

Is it possible to use Unicode normalization forms instead?

danny0838 commented 4 years ago

There are 4 types of Unicode normalization form, which could make things complicated. As it's unlikely used in Chinese texts (there's no definition of normalization form for IDS, at least currently), supporting of it can be deferred, I think.

buganini commented 4 years ago

FYI: Unicode normalization doesn't cover IDC

sgalal commented 4 years ago

Unicode normalization form is unrelated to this topic.

對於本題:支援 Unicode 組合字元會增加程式的複雜度;對使用者而言,使用時反而更有機會發生不可欲的後果。OpenCC 的轉換符合直覺,是以字元為基礎構造前綴樹匹配。若有特殊的需求,可以在 OpenCC 的基礎上自行編寫程式。

例如,對於待轉換文本,先使用上述程式碼將 IDS 與文本的其他部分分開。之後,文本的其他部分使用 OpenCC 轉換,IDS 部分單獨處理即可。

danny0838 commented 4 years ago

@sgalal 有具體實例嗎?基本上我想不出支援組合字會導致不可欲後果的情況。就算真的有,加一個選項切換以組合字為單位或以單一字元為單位也是可行的。

把 IDS 與其他文本分開是不可行的,如前所述,IDS 本身也是字,有可能本身需要簡繁轉換,也可能和前後文本相連成詞,要正確處理這些字或詞的轉換,唯有把邏輯直接內建在 OpenCC 本身。

sgalal commented 4 years ago

有具體實例嗎?基本上我想不出支援組合字元會導致不可欲後果的情況。

有!比如下面這句話:「表意文字描述字元 (Ideographic Description Character, IDC) 包括這些字元:⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻」。

把 IDS 與其他文本分開是不可行的,如前所述,IDS 本身也是字,有可能本身需要簡繁轉換,也可能和前後文本相連成詞,要正確處理這些字或詞的轉換,唯有把邏輯直接內建在 OpenCC 本身。

我卻認為,支援 IDS 等 Unicode 組合字元,會極大增加 OpenCC 程式的複雜度,不如單獨處理。

因此我的設想如下。例如

此县既有短狐之疾又有沙⿰虫风害人

一句,首先自行編寫程式,將此句斷開為

此县既有短狐之疾又有沙
⿰虫风
害人

三部分。然後一、三部分調用 OpenCC 轉換;第二部分識別為 IDS,自行編寫程式,轉換為「𧍯」。

最後合併三部分結果,得到

此縣既有短狐之疾又有沙𧍯害人
danny0838 commented 4 years ago

有具體實例嗎?基本上我想不出支援組合字元會導致不可欲後果的情況。

有!比如下面這句話:「表意文字描述字元 (Ideographic Description Character, IDC) 包括這些字元:⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻」。

這種情況是可以解套的,只要按照標準,把 IDS 限定在 漢字 | 部首 | 中日韓筆畫 | 私人造字區 | U+FF1F 裡即可,至於 IDS 匹配失敗時要怎麼處理可稍微想想,我認為從頭到匹配失敗處視為一個「字」即可,以此例來說就是「⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻」視為一個字,不會影響其他東西。

把 IDS 與其他文本分開是不可行的,如前所述,IDS 本身也是字,有可能本身需要簡繁轉換,也可能和前後文本相連成詞,要正確處理這些字或詞的轉換,唯有把邏輯直接內建在 OpenCC 本身。

我卻認為,加入 IDS 等 Unicode 組合字元,會極大增加 OpenCC 程式的複雜度,不如單獨處理。

因此我的設想如下。例如

此县既有短狐之疾又有沙⿰虫风害人

一句,首先自行編寫程式,將此句斷開為

此县既有短狐之疾又有沙
⿰虫风
害人

三部分。然後一、三部分調用 OpenCC 轉換;第二部分識別為 IDS,自行編寫程式,轉換為「𧍯」。

最後合併三部分結果,得到

此縣既有短狐之疾又有沙𧍯害人

假使要求「沙⿰虫风」轉成「沙虱」,但單獨的「⿰虫风」仍轉成「𧍯」,那要怎麼辦?這做法完全沒有解決組詞的問題啊。

buganini commented 4 years ago

另一個方向是先建立IDC->Codepoint的對照表,意義等於建立漢字的NFC資料庫,這本身也是值得獨立存在的資料。

有這個資料之後就可以把能轉回codepoint的IDC轉回去,轉不過去的也可以考慮先塞一些常用的到造字區,這樣opencc的修改可以最小化。

工具的部份,bsdconv應該可以幫上忙,bsdconv本身也有nfc/nfd/nfkc/nfkd/opencc的module。

buganini commented 4 years ago

https://github.com/ethantw/moedict-idc/blob/master/cjk-ext.tsv 這邊有一些IDC的資料

danny0838 commented 4 years ago

@buganini IDS 的主要存在意義就是用於表達目前沒有 unicode code point 的漢字,有 code point 的漢字早就直接用 code point 表達了(或是用程式按對照表全自動轉 code point)。

我同意 IDC->codepoint 有其價值,但在這個議題上恐怕幫助不大。

-- 至於 ICS <=> code point 的資料,引用的頁面有些似乎是非標準的。要說這方面的資料,CHISE project 做得更大。

buganini commented 4 years ago

如果把IDC定義到PUA上處理的話,PUA有6400+65536+65536=137472個codepoint,再不夠用的話,用64bits總夠了吧

danny0838 commented 4 years ago

如果把IDC定義到PUA上處理的話,PUA有6400+65536+65536=137472個codepoint,再不夠用的話,用64bits總夠了吧

我不懂這是什麼意思。意思是要自訂造字?

sgalal commented 4 years ago

經過思考,我贊同應該支援 Unicode 組合字元。

我提出一種方案如下,不知這樣如何?

對於 ideographic description characters

OpenCC 提供一個開關,當開關打開後,程式將「⿰虫风」視為一個處理單位,但預設詞庫中不添加「⿰虫风=>𧍯」這一轉換。

這是因為:

  1. 避免類似簡轉繁時「⿰虫风」變為「⿰蟲風」的錯誤
  2. 「⿰虫风」與異體字的地位類似。OpenCC 不處理異體字,因此預設不作轉換

對於 variation selectors

OpenCC 提供一個開關,當開關打開後,程式將「漢字+VS」視為一個處理單位,但預設詞庫中不添加「漢字+VS」的相應轉換,即轉換過後「漢字+VS」保持原樣不變。例如「劍」+ U+E0101,轉為簡體後保持「劍」+ U+E0101,而不變為「剑」。

這是因為:

  1. 避免類似繁轉簡時「劍」+ U+E0101 變為「剑」+ U+E0101 的錯誤
  2. 「劍」+ U+E0101 與異體字的地位類似。OpenCC 不處理異體字,因此預設不作轉換
danny0838 commented 4 years ago

經過思考,我贊同應該支援 Unicode 組合字元。

我提出一種方案如下,不知這樣如何?

對於 ideographic description characters

OpenCC 提供一個開關,當開關打開後,程式將「⿰虫风」視為一個處理單位,但預設詞庫中不添加「⿰虫风=>𧍯」這一轉換。

這是因為:

1. 避免類似簡轉繁時「⿰虫风」變為「⿰蟲風」的錯誤

2. 「⿰虫风」與異體字的地位類似。OpenCC 不處理異體字,因此預設不作轉換

對於 variation selectors

OpenCC 提供一個開關,當開關打開後,程式將「漢字+VS」視為一個處理單位,但預設詞庫中不添加「漢字+VS」的相應轉換,即轉換過後「漢字+VS」保持原樣不變。例如「劍」+ U+E0101,轉為簡體後保持「劍」+ U+E0101,而不變為「剑」。

這是因為:

1. 避免類似繁轉簡時「劍」+ U+E0101 變為「剑」+ U+E0101 的錯誤

2. 「劍」+ U+E0101 與異體字的地位類似。OpenCC 不處理異體字,因此預設不作轉換

整個方案沒提到 combining diacratic marks,我不知道是不打算支援還是怎樣?雖然它們並非用於漢字,但並非不可能發生。畢竟中英混雜的地區詞轉換如「B型肝炎=>乙型肝炎」並非不可能出現,這樣未來也就有可能出現類似「A̅并卵=>A̅併卵」、「♪͒組曲=>♪͒組曲」的轉換需求。

而 ideographic variation indicator (IVI) 是用於漢字的,「足」和「〾足」顯然不應該是同一個字,我想不出支援 IDS 卻不支援 IVI 的理由。(我認為 IVI 應該要允許出現在 IDS 之中,不過目前的 Unicode 標準似乎並未如此定義)

所以,如同我前面提出的演算法,我認為把 IDS, IVS, ideographic variation indicator 及所有 unicode combining char 全部視為同一種情況處理即可。最好是不加開關或直接作為預設值,因為未來有可能自然出現相關的轉換需求,如果不是預設值,未來一旦加入詞組就可能造成相容性問題。但現階段如果考量此功能實際上還不常用,或者開啟後可能明顯降低效能,或技術上還無法排除發生某些不可欲情況的可能性,暫且設為非預設值也可以。

同意預設詞庫不加「⿰虫风=>𧍯」或「𧍯=>⿰虫风」之類的轉換。這功能主要是允許使用者自行擴增相應的字詞庫,而不是預設提供這些罕用甚至不存在的字的轉換。

VS 的地位比較特別,按 Unicode 的定義應該是字義完全相等,只是書寫風格有異,因此視為同一個字有其合理性。不過 Unicode 對漢字的處理極複雜也極不統一,現階段可以先比照類似「戶戸户」被分開編碼把「漢字」和「漢字+VS」視為異體字關係及相異關係,反正使用者真有需求也可以自行添加諸如「劍\UE0101=>剑」的轉換。未來或許可以提供另一個開關讓 OpenCC 把「漢字」與「漢字+VS」視為同一字,不過不急著做。

預設字詞轉換表是否絕不提供「漢字+VS」版本我認為不急著說死,因為未來如果VS足夠普及,或許可能自然發生「漢字+特定VS」是特定地區用字(類似「戶」的情況)而需要特別轉換的字/詞組。當然這只是未雨綢繆,現階段應該還沒有必要特地把目前的轉換字詞表加上相應的VS版本。

buganini commented 4 years ago

如果把IDC定義到PUA上處理的話,PUA有6400+65536+65536=137472個codepoint,再不夠用的話,用64bits總夠了吧

我不懂這是什麼意思。意思是要自訂造字?

用PUA是為了讓OpenCC的修改最小化,維持一字一碼的邏輯,在內部邏輯把IDC轉為造字區的codepoint當作NFC用

danny0838 commented 4 years ago

如果把IDC定義到PUA上處理的話,PUA有6400+65536+65536=137472個codepoint,再不夠用的話,用64bits總夠了吧

我不懂這是什麼意思。意思是要自訂造字?

用PUA是為了讓OpenCC的修改最小化,維持一字一碼的邏輯,在內部邏輯把IDC轉為造字區的codepoint當作NFC用

恕我不明白要如何把不定長的IDS一對一serialize成造字區的code point。

我不是很清楚C語言的架構,但基本想法很簡單,本來是loop輸入字串裡的每個字元,現在改成以組合字元為單位把輸入字串和詞典切割成array,然後loop array裡的每個成份即可。如果要範例,我自己寫的 Python 程式已經實做出這功能了。

buganini commented 4 years ago

在C裡面要把處理primitive type換成處理不定長array頗麻煩的,當然不是做不出來,只是修改複雜度的問題,就算opencc原本的codepoint比對都有抽象層,還有GC的問題,簡單方法就是像python的immutable string一樣,只比對字串ID,而這個字串ID的概念其實就可以用PUA碼位實做

buganini commented 4 years ago

但OpenCC是C++寫的,應該也不用煩惱太多XD

另外想探討的是,IVS/Combining characters,是否適合用glyph的modifier的概念處理?甚至連換行符號都可以當作modifier,才不會因為換行而被強迫斷詞

danny0838 commented 4 years ago

我不熟C語言,關於如何實現的技術細節就不班門弄斧了,只要可行且無副作用就好。

但是,PUA 的做法槽點真的有點多……長度無限制的 IDS 如何映射到 PUA 碼點?若原來的文本和使用者的字詞轉表裡有 PUA 又要如何避免衝突? 如果說的是映射到 > 0x1FFFF 的「碼點」搞不好我還會覺得可行XD

後面所謂「適用 modifier 處理」具體是指什麼?在下慧根不足還請不吝多寫幾字說明下。

buganini commented 4 years ago

Sequence => codepoint 就是需要一個hash table就是了,碼位要放在哪都可以,如果怕跟user自己的PUA衝突,以C的手段來說,可以把資料型態換成64bits,但如果原本是wide char/wide string,那就是不好往這個方向擴充,反而用modifier容易

modifier是指資料的附加屬性,常見於按鍵event的表示,比如說Ctrl+Alt+S的資料為S,modifier為Ctrl+Alt,opencc裡的modifier則可以包含各種combining chars,或是標示此資料為opencc內部用的PUA,或是「此glyph後有\r\n」

buganini commented 4 years ago

其實IDS的collation跟modifier兩個方向也沒有衝突,兩個方向都做是最有彈性的,但把combining characters視為IDS的一部分會有些問題,一是這樣已經預設了glyph的等價條件,二是多個combining characters同時出現時有順序問題(NFD有處理)

danny0838 commented 4 years ago

Sequence => codepoint 就是需要一個hash table就是了,碼位要放在哪都可以,如果怕跟user自己的PUA衝突,以C的手段來說,可以把資料型態換成64bits,但如果原本是wide char/wide string,那就是不好往這個方向擴充,反而用modifier容易

IDS 是開放的,如何保證 hash table 能涵蓋所有使用者可能寫出的 IDS?難道使用者還要先自訂一個 IDS => code point 的轉換表才能讓 OpenCC 正確處理 IDS?

modifier是指資料的附加屬性,常見於按鍵event的表示,比如說Ctrl+Alt+S的資料為S,modifier為Ctrl+Alt,opencc裡的modifier則可以包含各種combining chars,或是標示此資料為opencc內部用的PUA,或是「此glyph後有\r\n」

combining diacratic marks 和 IVS 或許可以勉強視為 modifier,但是按這做法,modifier 也會變成不定長的資料,如果一開始的問題是 C 無法(便利地)處理不定長資料,這並沒有解決什麼問題啊。如果 C 可以處理不定長資料,那按我本來的演算法切成 string array 就可以了吧?

其實IDS的collation跟modifier兩個方向也沒有衝突,兩個方向都做是最有彈性的,但把combining characters視為IDS的一部分會有些問題,一是這樣已經預設了glyph的等價條件,二是多個combining characters同時出現時有順序問題(NFD有處理)

如果按前面所述要按 Unicode 標準實做 IDS,combining diacratic marks 應該不會被視作 IDS 的一部分。(標準沒把 ideographic 定義得很清楚,按一般理解似乎 IVI 和 IVS 都不能是 IDS 的一部分,但我認為兩者都應該要允許作為 IDS 的一部分,否則往往無法精準表示該字形。實際上有些 project 例如 CHISE 的 IDS 也額外使用了一些字元,不是完全按 Unicode 標準實做。或許必要時可提供另一參數允許使用者自訂 IDS 允許的字元)

多個 combining character 同時出現的順序問題確實是個問題。我的做法只是先避免諸如「黑桃A\u0300」被「黑桃A=>桃子A」轉換這種絕對不應該發生的狀況,至於像「a\u0300\u0320」、「a\u0320\u0300」是否兼容的問題則暫時沒處理,要認真解決這點應該確實要考慮 normalization 方針。不過以漢字處理來說,VS 不會有多個的問題(萬一使用者把漢字加上多個 VS,為了避免錯誤還是會整串視為一個「字」,但因為本來就是錯誤用法,所以不必考慮標準化和等價性的問題),combining character 是非漢字的東西,本來就不是 OpenCC 的主要工作範圍,是否要把 unicode 等價視為等價也不一定有標準答案,況且使用者如有需求可以在 OpenCC 外把輸入文本和轉換表自行做標準化,因此我覺得可以先不急著處理。

buganini commented 4 years ago

Sequence => codepoint 就是需要一個hash table就是了,碼位要放在哪都可以,如果怕跟user自己的PUA衝突,以C的手段來說,可以把資料型態換成64bits,但如果原本是wide char/wide string,那就是不好往這個方向擴充,反而用modifier容易

IDS 是開放的,如何保證 hash table 能涵蓋所有使用者可能寫出的 IDS?難道使用者還要先自訂一個 IDS => code point 的轉換表才能讓 OpenCC 正確處理 IDS?

不必涵蓋所有使用者可能寫出的,只要涵蓋opencc要處理的集合就好,這個hash table lookup應由opencc內部處理

modifier是指資料的附加屬性,常見於按鍵event的表示,比如說Ctrl+Alt+S的資料為S,modifier為Ctrl+Alt,opencc裡的modifier則可以包含各種combining chars,或是標示此資料為opencc內部用的PUA,或是「此glyph後有\r\n」

combining diacratic marks 和 IVS 或許可以勉強視為 modifier,但是按這做法,modifier 也會變成不定長的資料,如果一開始的問題是 C 無法(便利地)處理不定長資料,這並沒有解決什麼問題啊。如果 C 可以處理不定長資料,那按我本來的演算法切成 string array 就可以了吧?

對,但由於combining marks數量有限,可以是定長,定長至少可以閃掉GC的問題 string array另外的問題是效能,原本一個整數比對都要變成一個迴圈,雖然以opencc來說這點效能應該不是什麼嚴重的事情

其實IDS的collation跟modifier兩個方向也沒有衝突,兩個方向都做是最有彈性的,但把combining characters視為IDS的一部分會有些問題,一是這樣已經預設了glyph的等價條件,二是多個combining characters同時出現時有順序問題(NFD有處理)

如果按前面所述要按 Unicode 標準實做 IDS,combining diacratic marks 應該不會被視作 IDS 的一部分。(標準沒把 ideographic 定義得很清楚,按一般理解似乎 IVI 和 IVS 都不能是 IDS 的一部分,但我認為兩者都應該要允許作為 IDS 的一部分,否則往往無法精準表示該字形。實際上有些 project 例如 CHISE 的 IDS 也額外使用了一些字元,不是完全按 Unicode 標準實做。或許必要時可提供另一參數允許使用者自訂 IDS 允許的字元)

多個 combining character 同時出現的順序問題確實是個問題。我的做法只是先避免諸如「黑桃A\u0300」被「黑桃A=>桃子A」轉換這種絕對不應該發生的狀況,至於像「a\u0300\u0320」、「a\u0320\u0300」是否兼容的問題則暫時沒處理,要認真解決這點應該確實要考慮 normalization 方針。不過以漢字處理來說,VS 不會有多個的問題(萬一使用者把漢字加上多個 VS,為了避免錯誤還是會整串視為一個「字」,但因為本來就是錯誤用法,所以不必考慮標準化和等價性的問題),combining character 是非漢字的東西,本來就不是 OpenCC 的主要工作範圍,是否要把 unicode 等價視為等價也不一定有標準答案,況且使用者如有需求可以在 OpenCC 外把輸入文本和轉換表自行做標準化,因此我覺得可以先不急著處理。

對,我的意思就是說,先處理限縮範圍的glyph等價,用string array也可以做,用hash table是希望盡可能簡化修改幅度的想法,剩下對中文來說定義不明的部份則可以另外再用modifier處理,用modifier處理還可以保留是否要把combining characters當作等價條件的一部分的彈性

danny0838 commented 4 years ago

IDS 是開放的,如何保證 hash table 能涵蓋所有使用者可能寫出的 IDS?難道使用者還要先自訂一個 IDS => code point 的轉換表才能讓 OpenCC 正確處理 IDS?

不必涵蓋所有使用者可能寫出的,只要涵蓋opencc要處理的集合就好,這個hash table lookup應由opencc內部處理

使用者很難去確認 OpenCC 的 hash table 有什麼東西吧?這樣不就造成使用者自建詞庫用到的 IDS 可能無法預測地不能正常處理?我覺得這顯然是不應該採用的處理方案……。

modifier是指資料的附加屬性,常見於按鍵event的表示,比如說Ctrl+Alt+S的資料為S,modifier為Ctrl+Alt,opencc裡的modifier則可以包含各種combining chars,或是標示此資料為opencc內部用的PUA,或是「此glyph後有\r\n」

combining diacratic marks 和 IVS 或許可以勉強視為 modifier,但是按這做法,modifier 也會變成不定長的資料,如果一開始的問題是 C 無法(便利地)處理不定長資料,這並沒有解決什麼問題啊。如果 C 可以處理不定長資料,那按我本來的演算法切成 string array 就可以了吧?

對,但由於combining marks數量有限,可以是定長,定長至少可以閃掉GC的問題 string array另外的問題是效能,原本一個整數比對都要變成一個迴圈,雖然以opencc來說這點效能應該不是什麼嚴重的事情

我不曉得 OpenCC 目前內部是用 code point 還是 string,如果本來是用 code point 那就改成 code point array 也可以。

buganini commented 4 years ago

使用者不需要意識到hash table的存在才對,就像python string的hash table,user也不需要親自處理,但是id("ab")==id("a"+"b")

danny0838 commented 4 years ago

使用者不需要意識到hash table的存在才對,就像python string的hash table,user也不需要親自處理,但是id("ab")==id("a"+"b")

這不是重點吧。重點是支援 Unicode 組合字就是要支援所有情況,而不是讓使用者在詞庫加組合字都無法確定 OpenCC 能否正確處理吧?

buganini commented 4 years ago

python string也支援所有arbitrary data啊?為什麼hash table會有資料的限制?

danny0838 commented 4 years ago

python string也支援所有arbitrary data啊?為什麼hash table會有資料的限制?

「不必涵蓋所有使用者可能寫出的,只要涵蓋opencc要處理的集合就好」這句話是你說的吧?言下之意不就是會有一些使用者可能寫出的 IDS 是 OpenCC 不處理/無法正確處理的?

buganini commented 4 years ago

我的意思是說,Unicode PUA的定址空間大概足夠讓opencc詞庫和user自建詞庫的mapping使用,就算不夠,64bits也夠,不用去考慮理論上無限多種的組合,user自建詞庫也是由opencc讀取處理的,opencc可以自行調整hash table

實務上維護hash table的成本跟runtime比對array的成本哪個高,要由開發者判斷了

danny0838 commented 4 years ago

有這麼樂觀嗎?我們來試算一下……

64-bit unsigned int 最大值是 2^64 - 1,大約是 1.84E19。

Unicode 碼位上限是 0x10FFFF,想把 IDS 字串用一個整數表示,至少要 0x110000 進位,比方說把「⿱井蛙」(\u2FF1\u4E95\u86D9)轉成: 0x2FF1 0x110000^2 + 0x4E95 0x110000 + 0x86D9。

這樣做的話長度為 N 的 IDS 轉換成的整數最大值為 0x110000^N - 1。

0x110000^N - 1 的值在 N = 3 時為 1.38E18,在 N = 4 時為 1.54E24,已經超過上限。

所以用這種轉換方式,最多只能表達長度為 3 的 IDS。

如果改用 UTF-16,碼位上限是 0xFFFF,轉成整數用 0x10000 進位,N 字元的 IDS 最大值為 0x10000^N - 1。N = 4 時剛好達到上限 2^64 - 1。

若用這種序列化方式,最多只能表達 4 字元的 IDS。

而 IDS 只要稍微複雜一點就可以有 5 以上的長度,比如「⿱艹⿰白凡」長度是 5,「招財進寶」合文的一種寫法「⿳宀珤⿺辶⿲隹貝招」長度是 9,陝西BiángBiáng麵的一種寫法「⿺⻍⿱⿰⿳宀八⿰月⿱⿲糸言糸⿲長馬長戈心」長度是 20。

如果只能支援到 3 個 UTF-32 或 4 個 UTF-16 以內長度的 IDS,那是遠遠不敷一般使用的。

buganini commented 4 years ago

不需要這樣算,讓sequence對應到一個不重複數值就夠了 常見作法是hashcode+buckets或是trie

danny0838 commented 4 years ago

我已經是用最小不重複算法了。還有什麼可能確保不會重複的轉 64-bit 整數方法?

如果用像 sha-1 之類的 hash 那也是轉字串而非整數。

buganini commented 4 years ago

需要的是「對應」,不是「轉換」