寻找UTF8转UTF16的算法说明

问题描述 投票:0回答:2

我有 3 个字节表示以 utf8 编码的 unicode 字符。例如,我有

E2 82 AC
(UTF8) 代表 unicode 字符
€ (U+20AC)
。他们有什么算法可以进行这种转换吗?我知道它们是 Windows api MultiByteToWideChar 但我想知道它们是否是 E2 82 AC 和 U+20AC 之间的简单数学关系。 utf8 -> utf16 之间的映射是一个简单的数学函数还是一个硬编码的映射。

c++ delphi unicode utf-8 utf-16
2个回答
22
投票

只需一点数学知识就可以将有效 UTF-8 字节序列直接转换为 UTF-16。

验证 UTF-8 字节序列很简单:只需检查第一个字节是否与以下模式之一匹配,并且

(byte and $C0) = $80
对于序列中的每个后续字节都为 true。

UTF-8 序列中的第一个字节告诉您序列中有多少字节:

(byte1 and $80) = $00: 1 byte
(byte1 and $E0) = $C0: 2 bytes
(byte1 and $F0) = $E0: 3 bytes
(byte1 and $F8) = $F0: 4 bytes
anything else: error

有非常简单的公式可将 UTF-8 1 字节、2 字节和 3 字节序列转换为 UTF-16,因为它们都表示

U+10000
以下的 Unicode 代码点,因此可以按 UTF 中的原样表示-16 仅使用一个 16 位代码单元,无需代理,只需一些调整,例如:

1 字节:

UTF16 = UInt16(byte1 and $7F)

2字节:

UTF16 = (UInt16(byte1 and $1F) shl 6)
        or UInt16(byte2 and $3F)

3字节:

UTF16 = (UInt16(byte1 and $0F) shl 12)
        or (UInt16(byte2 and $3F) shl 6)
        or UInt16(byte3 and $3F)

另一方面,将 UTF-8 4 字节序列转换为 UTF-16 则稍微复杂一些,因为它表示

U+10000
或更高的 Unicode 代码点,因此需要使用 UTF-16 代理项,这需要一些额外的数学来计算,例如:

4字节:

CP = (UInt32(byte1 and $07) shl 18)
     or (UInt32(byte2 and $3F) shl 12)
     or (UInt32(byte3 and $3F) shl 6)
     or UInt32(byte4 and $3F)
CP = CP - $10000
highSurrogate = $D800 + UInt16((CP shr 10) and $3FF)
lowSurrogate = $DC00 + UInt16(CP and $3FF)
UTF16 = highSurrogate, lowSurrogate

现在,话虽如此,让我们看看你的例子:

E2 82 AC

第一个字节是

($E2 and $F0) = $E0
,第二个字节是
($82 and $C0) = $80
,第三个字节是
($AC and $C0) = $80
,所以这确实是一个有效的UTF-8 3字节序列。

将这些字节值代入 3 字节公式,您将得到:

UTF16 = (UInt16($E2 and $0F) shl 12)
        or (UInt16($82 and $3F) shl 6)
        or UInt16($AC and $3F)

      = (UInt16($02) shl 12)
        or (UInt16($02) shl 6)
        or UInt16($2C)

      = $2000
        or $80
        or $2C

      = $20AC

事实上,Unicode 代码点

U+20AC
在 UTF-16 中编码为
$20AC


0
投票

雷米·勒博,非常感谢。

我的西里尔字符问题已解决!)

在我的例子中,所有字符都由 2 个字节表示(我使用 processing)。这是一个问题。谢谢你,我写了一个函数

String decode(String s){
  String d = "";
  for (int i = 0; i < s.length(); i++){
    if(s.charAt(i) > 127){               // check if it not ASCII symbol
      int b = int(s.charAt(i));          // get char1
      int c = b & 224;                   // char1 and 0xE0
      if (c == 192){                     // if its equals 0xC0 - do 2 bytes algorithm
        b = b & 31;                      // char1 and 0x1F
        b = b << 6;                      // char1 shift left by 6
        i++;                             // index to next char
        c = int(s.charAt(i));            // take char2
        c = c & 63;                      // char2 and 0x3F
        b = b | c;                       // char1 = char1 or char2 
        d += char(b);                    // add char1 to output string (char2 skiped)
      }
    }
    else
      d += s.charAt(i);                  // if its ASCII symbol juast add it to output string
  }
  return d;
}

及其工作!)

© www.soinside.com 2019 - 2024. All rights reserved.