簡體   English   中英

截斷 UTF-16 字符串

[英]Truncating a UTF-16 string

我維護一個 Python 庫,它驗證並准備下游 Java 服務的輸入。 因此,圖書館內的預驗證需要與下游服務保持一致。 這里的一個痛點是計算某些 Unicode 字符串的字符串長度。

Python 計算字符數以確定字符串的長度,而 Java 計算代碼單元(即 UTF-16 代理項對)。 通常這些計算是相同的,但在基本多語言平面之外,這些計算可能不同。 例如,字符串“wink”在 Python 中的長度為 6,在 Java 中的長度為 7(表情符號為 2 + 其他字符為 5)。

因此,要復制 Java 的長度計算方法,我們需要編碼為 UTF-16,然后除以 2:

field_value = "wink 😉"    
len(field_value.encode("utf-16-le")) // 2

但是,如果我想根據 UTF-16 代碼對方法將輸入字符串截斷為最大允許字符限制,這將更具挑戰性。 轉換為 UTF-16 然后切片過於熱心,因為並非所有字符都在 BMP 之外:

field_value = "wink 😉"  
field_value.encode("utf-16-le")[:LIMIT].decode("utf-16-le", "ignore")

在 Python 中根據此字符權重截斷 Unicode 字符串(包含 BMP + 后 BMP 字符)的有效方法是什么?

這里有一個 function 將在字符串中的有效代碼點截斷。 它的工作原理是測試太長的字符串不會在代理對的中間被截斷。 它基於我對截斷 UTF-8 的類似回答 請注意,這不處理字形。 如果需要測試截斷修飾符,您可以使用unicodedata.category()

s = 'A 😉 short 😉😉 test'

def utf16_trailing_surrogate(b):
    '''The high byte of a UTF-16 trailing surrogate starts with the bits 110111xx.'''
    return (b & 0b1111_1100) == 0b1101_1100

def utf16_byte_truncate(text, max_bytes):
    '''If text[max_bytes:max_bytes+1] is a trailing surrogate, back up two bytes and truncate.
    '''
    i = max_bytes - max_bytes % 2  # make even
    utf16 = text.encode('utf-16le')
    if len(utf16) <= i: # does it fit
        return utf16
    if utf16_trailing_surrogate(utf16[i+1]):
        i -= 2
    return utf16[:i]

# test for various max_bytes:
for m in range(len(s.encode('utf-16le'))+1):
    b = utf16_byte_truncate(s,m)
    print(f'{m:2} {len(b):2} {b.decode("utf-16le")!r}')

Output:

 0  0 ''
 1  0 ''
 2  2 'A'
 3  2 'A'
 4  4 'A '
 5  4 'A '
 6  4 'A '
 7  4 'A '
 8  8 'A 😉'
 9  8 'A 😉'
10 10 'A 😉 '
11 10 'A 😉 '
12 12 'A 😉 s'
13 12 'A 😉 s'
14 14 'A 😉 sh'
15 14 'A 😉 sh'
16 16 'A 😉 sho'
17 16 'A 😉 sho'
18 18 'A 😉 shor'
19 18 'A 😉 shor'
20 20 'A 😉 short'
21 20 'A 😉 short'
22 22 'A 😉 short '
23 22 'A 😉 short '
24 22 'A 😉 short '
25 22 'A 😉 short '
26 26 'A 😉 short 😉'
27 26 'A 😉 short 😉'
28 26 'A 😉 short 😉'
29 26 'A 😉 short 😉'
30 30 'A 😉 short 😉😉'
31 30 'A 😉 short 😉😉'
32 32 'A 😉 short 😉😉 '
33 32 'A 😉 short 😉😉 '
34 34 'A 😉 short 😉😉 t'
35 34 'A 😉 short 😉😉 t'
36 36 'A 😉 short 😉😉 te'
37 36 'A 😉 short 😉😉 te'
38 38 'A 😉 short 😉😉 tes'
39 38 'A 😉 short 😉😉 tes'
40 40 'A 😉 short 😉😉 test'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM