5

我从 TCP 服务器接收到一个字节流缓冲区,其中可能包含形成 unicode 字符的多字节字符。我想知道是否总是有办法检查 BOM 来检测这些字符,或者你想怎么做?

4

5 回答 5

9

如果您知道数据是 UTF-8,那么您只需检查高位:

  • 0xxxxxxx = 单字节 ASCII 字符
  • 1xxxxxxx = 多字节字符的一部分

或者,如果您需要区分前导/尾随字节:

  • 10xxxxxx = 多字节字符的第 2、第 3 或第 4 个字节
  • 110xxxxxx = 2 字节字符的第一个字节
  • 1110xxxx = 3 字节字符的第一个字节
  • 11110xxx = 4 字节字符的第一个字节
于 2011-02-16T06:41:04.043 回答
3

在 UTF-8 中,任何具有第 8 位的内容都是多字节代码点的一部分。所以基本上检查(0x80 & c)!=0每个字节是最简单的方法。

于 2011-02-19T19:34:13.240 回答
2

有很多方法可以检测多字节字符,不幸的是……它们都不可靠。

如果这是返回的 Web 请求,请检查标头,因为 Content-Type 标头通常会指示页面编码(这可能表示存在多字节字符)。

您还可以检查 BOM,因为它们是无效字符,它们不应该出现在普通文本中,因此查看它们是否存在不会有什么坏处。但是,它们是可选的,并且很多时候不会出现(取决于实现、配置等)。

于 2011-02-16T05:34:08.343 回答
1

BOM 大多是可选的。如果您从中接收的服务器提供多字节字符,它可能会假设您知道这一点,并为 BOM 保存 2 个字节。您是否在寻求一种方法来判断您收到的数据是否可能是多字节字符串?

于 2011-02-16T05:32:16.100 回答
0

让我实现dan04的答案

此后我使用 C++14。如果只能使用旧版本的 C++,则必须将二进制文字(例如0b10)重写为整数文字(例如2)。

执行

int is_utf8_character(unsigned char c) { //casts to `unsigned char` to force logical shifts
    if ((c >> 7) == 0b1) {
        if ((c >> 6) == 0b10) {
            return 2; //2nd, 3rd or 4th byte of a utf-8 character
        } else {
            return 1; //1st byte of a utf-8 character
        }
    } else {
        return 0; //a single byte character (not a utf-8 character)
    }
}

例子

代码

using namespace std;
#include <iostream>

namespace N {

    int is_utf8_character(unsigned char c) { //casts to `unsigned char` to force logical shifts
        if ((c >> 7) == 0b1) {
            if ((c >> 6) == 0b10) {
                return 2; //2nd, 3rd or 4th byte of a utf-8 character
            } else {
                return 1; //1st byte of a utf-8 character
            }
        } else {
            return 0; //a single byte character (not a utf-8 character)
        }
    }

    unsigned get_string_length(const string &s) {
        unsigned width = 0;
        for (int i = 0; i < s.size(); ++i) {
            if (is_utf8_character(s[i]) != 2) {
                ++width;
            }
        }
        return width;
    }

    unsigned get_string_display_width(const string &s) {
        unsigned width = 0;
        for (int i = 0; i < s.size(); ++i) {
            if (is_utf8_character(s[i]) == 0) {
                width += 1;
            } else if (is_utf8_character(s[i]) == 1) {
                width += 2; //We assume a multi-byte character consumes double spaces than a single-byte character.
            }
        }
        return width;
    }

}

int main() {

    const string s = "こんにちはhello"; //"hello" is "こんにちは" in Japanese.

    for (int i = 0; i < s.size(); ++i) {
        cout << N::is_utf8_character(s[i]) << " ";
    }
    cout << "\n\n";

    cout << "       Length: " << N::get_string_length(s) << "\n";
    cout << "Display Width: " << N::get_string_display_width(s) << "\n";

}

输出

1 2 2 1 2 2 1 2 2 1 2 2 1 2 2 0 0 0 0 0 

       Length: 10
Display Width: 15
于 2020-03-22T10:42:36.197 回答