尽管在论坛上关于unicode和字符串转换(在C/C++中)以及谷歌搜索了几个小时的话题,仍然找不到对我看来像是一个非常基本的过程的直接解释。这是我想要做的:字符串到Unicode和Unicode到十进制代码点(C++)
我有一个字符串,它可能使用任何可能的语言的任何字符。以西里尔文为例。所以说我有:
std::string str = "сапоги";
我想遍历每个字符组成该字符串和:
- 知道/打印字符的Unicode值
- 转换是Unicode值的十进制值
我真的谷歌搜索几个小时,并找不到直接的答案。如果有人能告诉我如何做到这一点,那会很好。
编辑
所以我设法得到那么远:
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <locale>
#include <codecvt>
#include <iomanip>
// utility function for output
void hex_print(const std::string& s)
{
std::cout << std::hex << std::setfill('0');
for(unsigned char c : s)
std::cout << std::setw(2) << static_cast<int>(c) << ' ';
std::cout << std::dec << '\n';
}
int main()
{
std::wstring test = L"сапоги";
std::wstring_convert<std::codecvt_utf16<wchar_t>> conv1;
std::string u8str = conv1.to_bytes(test);
hex_print(u8str);
return 1;
}
结果:
04 41 04 30 04 3f 04 3e 04 33 04 38
哪个是正确的(它映射到Unicode)。问题是我不知道我是否应该使用utf-8,16或其他内容(正如克里斯在评论中指出的那样)。有没有一种方法可以找到这个问题? (无论编码它最初使用或需要使用的任何编码?)
EDIT 2
我想我会解决一些与第二编辑评论:
“转换的是Unicode值到十进制值“为什么?
我会解释为什么,但我也想以友好的方式发表评论,我的问题不是'为什么',而是'如何';-)。你可以假设OP有提出这个问题的理由,但是当然,我知道人们为什么好奇......所以让我解释一下。我之所以需要这一切,是因为我最终需要从字体文件中读取字形(TrueType OpenType无关紧要)。碰巧这些文件有一个名为cmap
的表,它是某种类型的关联数组,将字符的值(在代码点上的表单中)映射到字体文件中字形的索引。表中的代码点没有使用符号U + XXXX定义,而是直接在该数字的小数对应中定义(假设U + XXXX表示法是uint16数字的十六进制表示法[或者如果大于uint16,则为U + XXXXXX但更多在那之后])。因此总之,西里尔语([gueu])中的字母г
具有代码点值U+0433
,其十进制形式是1075
。我需要值1075
在cmap
表中进行查找。
// utility function for output
void hex_print(const std::string& s)
{
std::cout << std::hex << std::setfill('0');
uint16_t i = 0, dec;
for(unsigned char c : s) {
std::cout << std::setw(2) << static_cast<int>(c) << ' ';
dec = (i++ % 2 == 0) ? (c << 8) : (dec | c);
printf("Unicode Value: U+%04x Decimal value of code point: %d\n", codePoint, codePoint);
}
}
的std :: string被编码无关。它基本上存储字节。 std :: wstring很奇怪,虽然也没有被定义为保存任何特定的编码。在Windows中,wchar_t用于UTF-16
是的,我想当你理解“while”时你认为(至少我做过)字符串只是存储“ASCII”字符(在此处保留) ,这似乎是错误的。事实上,std :: string只是注释中的字节。虽然很明显,如果你看一下串english
的字节你:
std::string eng = "english";
hex_print(eng);
65 6e 67 6c 69 73 68
,如果你做“同样的事情сапоги你:
std::string cyrillic = "сапоги";
hex_print(cyrillic);
d1 81 d0 b0 d0 bf d0 be d0 b3 d0 b8
我真的很想知道/理解是如何隐式完成这种转换?为什么UTF-8编码在这里而不是UTF-16,并且是否有可能改变(或者是由我的IDE或OS定义的)?显然,当我复制粘贴字符串在我的文本编辑器中,它实际上已经复制了一个12字节的数组(这12个字节可能是utf-8或utf-16)
我认为Unicode和编码之间存在混淆。 Codepoint(AFAIK)只是一个字符代码。 UTF 16给你的代码,所以你可以说你的0x0441是西里尔小写字母的情况下的一个代码点。据我了解,UTF16与Unicode代码点一对一映射,其范围为1M和某些字符。但是,其他编码技术(例如UTF-8)不会直接映射到Unicode代码点。所以我猜,你最好坚持使用UTF-16
没错!我发现这个评论确实非常有用。因为是的,在编码Unicode代码点值的方式与Unicode值本身无关的事实方面存在着混淆(而且我感到困惑),很好,因为事实上,事情可能会误导我,因为我会现在显示。 You can indeed encode the string сапоги
using UTF8 and you will get:
d1 81 d0 b0 d0 bf d0 be d0 b3 d0 b8
所以很明显它无关确实字形的Unicode值。现在,如果你使用UTF-16编码相同的字符串你:
04 41 04 30 04 3f 04 3e 04 33 04 38
其中04和41是真的信с
(西里尔[SE])的两个字节(十六进制形式)。至少在这种情况下,unicode值和其uint16表示形式之间存在直接映射关系。这就是为什么(每维基的解释 [source]):
两个UTF-16和UCS-2在该范围内为单16位代码的单位,在数值上等于相应的码点编码的代码点。
但是正如有人在评论中提出的那样,某些代码点值超出了可以用2个字节定义的值。例如:
1D307 TETRAGRAM FOR FULL CIRCLE(Tai Xuan Jing Symbols)
这正是该评论是在暗示:
据我所知,除非你使用代理UTF-16并没有涵盖所有的字符对。它意味着原来,当65K是绰绰有余,但出去的窗口,使之成为一个非常尴尬的选择,现在
虽然是完全准确的UTF-16一样UTF-8 CAN编码所有字符,尽管它可以使用最多4个字节(因为您建议如果需要超过2个字节将使用代理对)。
我试图使用mbrtoc32
做一个转换为UTF-32,但在Mac上奇怪地缺少cuchar
。
顺便说一句,如果你不知道什么是surrogate pair
是(我没有)有a nice post about this on the forum。
你想使用像'std :: string str = L“сапоги”'? –
我不知道。我的目标是找到组成字符串的每个字符的Unicode值,并将其转换为十进制值。 – user18490
这是一个很好的阅读:http://reedbeta.com/blog/programmers-intro-to-unicode/ – tntxtnt