我目前正在编写自己的支持表情符号的DrawTextEx()函数。使用此函数,每次在文本中找到表情时都会调用回调,从而使调用者有机会将包含表情的文本段替换为图像。例如,在绘制文本时,文本中的Unicode字符0x3DD8 0x00DE将替换为笑脸图像。实际上,这个函数运行得很好。
现在我想在调用者端实现一个图像库。我在回调函数中收到一个类似0x3DD8 0x00DE的文本段,我的想法是将此代码用作包含所有Unicode组合的map中的键,每个组合都与包含要绘制的图像的结构相链接。我在http://emojione.com/developers/网站上找到了一个很好的包。本网站提供的所有软件包都包含多个文件名,即十六进制代码。因此,我可以迭代包中包含的文件,并以自动方式创建我的地图。
然而,我发现这些代码是另一个标准的一部分,实际上是一组名为"HTML entity“的项目,显然是在web开发中使用的,正如可以在http://graphemica.com/%F0%9F%98%80网站上看到的那样。因此,为了能够使用这些文件,我需要一个解决方案来将它们名称中包含的HTML实体值转换为UTF16代码。例如,对于上面提到的笑脸,我需要将0x1f600HTML实体代码转换为0x3DD8 0x00DE UTF16代码。
一种蛮力方法可能包括编写一个映射来转换这些代码,将它们逐个添加到我的代码中。但由于Unicode标准在最乐观的情况下包含1800多个表情符号组合,我想知道有一个现有的解决方案,例如已知的API或函数,我可以使用它来完成这项工作。或者有什么已知的诀窍可以做到这一点?(例如,"character + ('a‘- 'A')“将大写字符转换为小写字符)
问候
发布于 2016-09-21 07:08:16
例如,在文本中找到的Unicode字符0x3DD8 0x00DE将替换为笑脸图像
字符U+1F600笑脸由UTF-16代码单元序列0xD83D,0xDE00表示。
(Graphemica交换每个代码单元的字节顺序是极具误导性的;忽略这一点。)
我发现这些代码是另一个标准的一部分,实际上是一组名为"HTML entity“的项目,显然是在web开发中使用的
HTML与此无关。它们是普通的Unicode字符--在基本的多语言平面之外,在U+FFFF之上,这就是为什么需要不止一个UTF16编码单元来表示它们的原因。
像😀这样的HTML数字字符引用(通常被错误地称为实体)是一种通过代码点编号引用字符的方法,但是转义字符串只在HTML (或XML)文档中有效,而我们并不在其中。
所以:
我需要将0x1f600
实体代码转换为0x3DD8 0x00DE UTF16代码。
听起来更像是:
我需要转换U+1F600 point的表示:从码位编号0x1F600到UTF-16编码单元序列0xD83D,0xDE00
在C#中是:
string face = Char.ConvertFromUtf32(0x1F619); // "" aka "\uD83D\uDE00"或者在另一个方向:
int codepoint = Char.ConvertToUtf32("\uD83D\uDE00", 0); // 0x1F619(“UTF-32”这个名字在这里选得不好;我们讨论的是一个整数码位数,而不是每个字符四个字节的序列。)
,或者有什么已知的技巧可以做到这一点?(例如,"character + ('a‘- 'A')“将大写字符转换为小写字符)
在C++中,事情更烦人;没有(我能想到的)在代码点和UTF16代码单元之间直接转换的东西。您可以使用各种编码函数/库在UTF32编码的字节序列和UTF16代码单元之间进行转换,但这样做的结果可能比自己编写conversion logic更快。例如,以最基本的形式表示单个字符:
std::wstring fromCodePoint(int codePoint) {
if (codePoint < 0x10000) {
return std::wstring(1, (wchar_t)codePoint);
}
wchar_t codeUnits[2] = {
0xD800 + ((codePoint - 0x10000) >> 10),
0xDC00 + ((codePoint - 0x10000) & 0x3FF)
};
return std::wstring(codeUnits, 2);
}这是假设wchar_t类型基于UTF-16代码单元,与C#的string类型相同。在Windows上这可能是真的。在其他地方可能不是这样,但在wchar_t基于代码点的平台上,您可以将每个代码点作为一个字符从字符串中提取出来,而不需要进一步的处理。
(优化和错误处理留给读者作为练习。)
发布于 2016-09-21 22:53:55
我使用的是RAD Studio编译器,幸运的是,它为bobince提到的ConvertFromUtf32和ConvertToUtf32函数提供了一个实现。我对它们进行了测试,它们完全符合我的需求。
对于那些不使用Embarcadero产品的人来说,由bobince提供的fromCodePoint()实现也可以很好地工作。下面是在RAD Studio中实现并转换为C++的ConvertFromUtf32()函数
std::wstring ConvertFromUtf32(unsigned c)
{
const unsigned unicodeLastChar = 1114111;
const wchar_t minHighSurrogate = 0xD800;
const wchar_t minLowSurrogate = 0xDC00;
const wchar_t maxLowSurrogate = 0xDFFF;
// is UTF32 value out of bounds?
if (c > unicodeLastChar || (c >= minHighSurrogate && c <= maxLowSurrogate))
throw "Argument out of range - invalid UTF32 value";
std::wstring result;
// is UTF32 value a 16 bit value that can fit inside a wchar_t?
if (c < 0x10000)
result = wchar_t(c);
else
{
// do divide in 2 chars
c -= 0x10000;
// convert code point value to UTF16 string
result = wchar_t((c / 0x400) + minHighSurrogate);
result += wchar_t((c % 0x400) + minLowSurrogate);
}
return result;
}感谢博本斯的回答,他为我指明了正确的方向,并帮助我解决了这个问题。
问候
https://stackoverflow.com/questions/39604217
复制相似问题