使用 C++ 测试 Unicode 代码点是不是在 ISO-8859-5 集中

Posted

技术标签:

【中文标题】使用 C++ 测试 Unicode 代码点是不是在 ISO-8859-5 集中【英文标题】:Test if a Unicode code point is in the ISO-8859-5 set using C++使用 C++ 测试 Unicode 代码点是否在 ISO-8859-5 集中 【发布时间】:2016-04-27 17:48:00 【问题描述】:

ISO-8859-5 标准是 unicode 字符集的子集。我想测试 C++ 中 ISO-8859-5 的字符子集是否支持 unicode 字符。为此,我想在下面编写一个类似 isLegal 的函数,以便以下代码过滤掉非 ISO-8859-5 字符。

假设 wstring 来自一个 unicode 编码的字符串。

wstring str = L"AåБ????????????0";
vector<char32_t> bytes(str.begin(), str.end());
for (vector<char32_t>::const_iterator i = bytes.begin(); i != bytes.end(); ++i)
if (isLegal(*i, "ISO-8859-5"))

  std::cout << (*i) << ' ';

这样做的原因是我想将受支持的字符限制为 unicode 超集的子集,以便用户无法提交像表情符号这样的字符和不受支持的语言的字符。感谢您的帮助。

有没有一种简单的方法可以做到这一点。使用例如编解码器或类似的东西。例如,我知道 Qt 中的一个函数,在这方面有什么可以帮助我的吗?

QTextCodec *codec = QTextCodec::codecForName("ISO 8859-5");

或者也许有一个图书馆可以为我做这件事。

注意:为什么我使用 wstring?我的理解是 unicode 字符每个字符使用 1 到 4 个字节。这是字符的二进制表示,与呈现字符时不同。 std:string 支持多字节字符串,但是当您尝试隔离单个字符时,我不知道字符从哪里开始以及在哪里结束,因为每个字符中的字节宽度不一致。

所以我使用编解码器将多字节字符串解码为在 wchar_t 上模板化的 std::wstring。 Linux 上的 wchar_t 是 4 个字节宽,因此每个字符都有一致的宽度。因此,如果将多字节 unicode 集放入 wstring 中,则可以更轻松地识别每个字符,因为每个字符的 4 字节宽度都是一致的,并且所有 unicode 字符都将适合 4 位宽度,因此 wstring 可以处理来自unicode。

【问题讨论】:

我投票结束这个问题,因为它只是一个“给我代码”。 对于一次性的“手动比较”代码可能比通用编解码器例程更有效。例如:由于您只有一个目标编码要测试,因此您不必遍历其每个字符。您可以根据其(已翻译!)Unicode 值对 Cyrillic 编码进行排序,然后对每个输入字符有效地使用二进制查找。 (顺便投票重开??????) @ND: "我想支持编解码器 ISO 8859-1 到 ISO 8859-5" 这些是非常不同的东西,你需要一个单独的函数来检查每一个人。事实上,为 Latin-1 编写一个检查器是微不足道的,而为拉丁/西里尔语编写一个检查器要困难得多。 @ND: "所以你是说 char32_t 太宽了?" 我是说它不是 UTF-8。您的问题应该是关于如何测试 UTF-8 字符序列是否包含超出特定范围的代码点。您的代码实际上并没有在任何地方使用 UTF-8。那么你的问题到底是什么?就好像有人问了OpenGL,然后贴了一堆D3D代码。 谢谢。我不知道代码单元和点之间的区别。我的理解是 UTF-8 字符每个字符使用 1 到 4 个字节。这是字符的二进制表示,与呈现字符时不同。 std::wstring 以 wchar_t 为模板。 Linux 上的 wchar_t 为 4 字节宽,因此如果您将多字节 UTF-8 集放入 wstring 中,您可以更轻松地识别每个字符,因为每个字符的 4 字节宽度都是一致的,并且所有 UTF-8 字符都适合 4 位宽度,以便 wstring 处理来自 UTF-8 的任何可能的字符。 【参考方案1】:

没有用于字符代码转换的标准 C++ 库。事实上,我认为 C++ 实现甚至不需要知道不止一种编码。所以任何解决方案都需要一个库,或者手工编写的代码(即一个大的switch...)。

既然你提到了Qt,那么是的,你应该可以使用QTextCodec::canEncode

#include <QDebug>
#include <QTextCodec>

#include <string>

int main() 
    std::wstring const str = L"AåБ???0";
    auto const *codec = QTextCodec::codecForName("ISO-8859-5");
    if (!codec) 
        qFatal("Codec not found");
    

    qDebug() << "Using codec" << qPrintable(codec->name());

    for (auto c: str) 
        if (codec->canEncode(c))
            qDebug() << c;
    

但这给了我

Using codec ISO-8859-5
65
229
1041
128512
128580
128545
48

所以这是一个非解决方案。

【讨论】:

虽然canEncode 确实被破坏了,但您传递的c 需要一个代理对来表示。您需要将它们作为以QString 编码的代理对传递。您需要使用 QChar 代理逻辑来检查给定的 UCS-4 c 是否可以表示为单个 QChar 或代理对,然后从那里开始。 @Kuba - 我没有发现那些在 BMP 之外。感谢您的澄清。 我不确定的一件事是 L"foo" 是否期望 "foo" 是 UTF-8 或什么 :( 似乎是根据 this answer 定义的实现。【参考方案2】:

目前我正在使用此自定义解决方案:

#include <vector>
#include <string>
#include <boost/assign/std/vector.hpp>

using namespace std;
using namespace boost::assign; 

bool isIntInSet(int val, std::vector<int> set)
  if (std::find(set.begin(), set.end(), val) != set.end())
  
    return true;
  
  return false;


bool isLegal(int val, string isoNum)
  const string ISO8859_5 = "ISO8859-5";
  if (ISO8859_5 == isoNum)
    vector<int> isoSet5;
    isoSet5 += 0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002A,0x002B,0x002C,0x002D,0x002E,0x002F,0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003A,0x003B,0x003C,0x003D,0x003E,0x003F,0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F,0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005A,0x005B,0x005C,0x005D,0x005E,0x005F,0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007A,0x007B,0x007C,0x007D,0x007E,0x00A0,0x0401,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,0x0408,0x0409,0x040A,0x040B,0x040C,0x00AD,0x040E,0x040F,0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,0x041E,0x041F,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,0x042E,0x042F,0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,0x043E,0x043F,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,0x044E,0x044F,0x2116,0x0451,0x0452,0x0453,0x0454,0x0455,0x0456,0x0457,0x0458,0x0459,0x045A,0x045B,0x045C,0x00A7,0x045E,0x045F;
    if (isIntInSet(val, isoSet5))return true;
  
  return false;

通过查找http://czyborra.com/charsets/iso8859.html 上的可见字符集列表,每个字符集不包括控制字符,因此这不是完整的 ISO8859-5 字符列表,但对于所有可打印字符来说似乎已经足够了。

【讨论】:

因为所有 ISO-8859-1 集都是 ASCII(包括控制字符)的扩展,UTF-8 也是如此(当您忽略前导零时,Unicode 本身也是如此)。因此,您可能希望将该测试分成两部分,快速检查 &lt;128 并查找其余部分。 好主意。这样会更有效率。

以上是关于使用 C++ 测试 Unicode 代码点是不是在 ISO-8859-5 集中的主要内容,如果未能解决你的问题,请参考以下文章

将 unicode 代码点转换为 utf-16

在 C++ 源代码中使用 Unicode

Unicode 是不是有定义的最大代码点数?

测试一个字符是不是是有效的 Julia 单字符变量名?

除了使用字符串文字之外,还有其他方法可以在 C 中指定或输入 Unicode 代码点吗?

Perl 正则表达式匹配大型 Unicode 代码点