测试char*字符串是否包含多字节字符

6

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


1
什么编码?UTF-8?GB18030?有没有在任何地方声明过? - dan04
最有可能的我认为是UTF-8。 - cpx
5个回答

10

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

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

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

  • 10xxxxxx = 多字节字符的第2、3或4个字节
  • 110xxxxx = 2字节字符的第1个字节
  • 1110xxxx = 3字节字符的第1个字节
  • 11110xxx = 4字节字符的第1个字节

3

在UTF-8中,任何开头为8位的字节都属于多字节码点。因此,基本上检查每个字节的(0x80 & c)!=0就是最简单的方法。


2

让我实现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

2

检测多字节字符有很多方法,但不幸的是...没有一种是可靠的。

如果这是一个网络请求返回的结果,请检查头部信息,因为Content-Type头部通常会指示页面编码(这可以表明多字节字符是否存在)。

您也可以检查BOM(字节顺序标记),因为它们是无效字符,正常文本中不应该出现,因此检查一下是否存在也无妨。然而,它们是可选的,并且许多情况下可能不存在(取决于实现、配置等)。


2
U+FEFF作为零宽不间断空格的用法已经被弃用,但仍然非常有效。您可能会在普通文本中遇到它。实际上,这里就有一个例子:" "。 - Fred Nurk
1
在UTF中有效,但在ASCII中可能会出现问题。但你是正确的,我应该更明确我的意思。我并不是想说“无效”,而是更多地表示“通常不会在普通文本序列中遇到,除非用作BOM”。 - Washu

1

BOM通常是可选的。如果您从接收多字节字符的服务器接收数据,它可能会假定您知道这一点,并保存2个字节的BOM。您是否正在寻找一种方法来确定您接收到的数据是否可能是多字节字符串?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接