2017-03-05 176 views
1

尽管在论坛上关于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 

Code

哪个是正确的(它映射到Unicode)。问题是我不知道我是否应该使用utf-8,16或其他内容(正如克里斯在评论中指出的那样)。有没有一种方法可以找到这个问题? (无论编码它最初使用或需要使用的任何编码?)

EDIT 2

我想我会解决一些与第二编辑评论:

“转换的是Unicode值到十进制值“为什么?

我会解释为什么,但我也想以友好的方式发表评论,我的问题不是'为什么',而是'如何';-)。你可以假设OP有提出这个问题的理由,但是当然,我知道人们为什么好奇......所以让我解释一下。我之所以需要这一切,是因为我最终需要从字体文件中读取字形(TrueType OpenType无关紧要)。碰巧这些文件有一个名为cmap的表,它是某种类型的关联数组,将字符的值(在代码点上的表单中)映射到字体文件中字形的索引。表中的代码点没有使用符号U + XXXX定义,而是直接在该数字的小数对应中定义(假设U + XXXX表示法是uint16数字的十六进制表示法[或者如果大于uint16,则为U + XXXXXX但更多在那之后])。因此总之,西里尔语([gueu])中的字母г具有代码点值U+0433,其十进制形式是1075。我需要值1075cmap表中进行查找。

// 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

+0

你想使用像'std :: string str = L“сапоги”'? –

+0

我不知道。我的目标是找到组成字符串的每个字符的Unicode值,并将其转换为十进制值。 – user18490

+1

这是一个很好的阅读:http://reedbeta.com/blog/programmers-intro-to-unicode/ – tntxtnt

回答

2

为了您的目的,查找并打印每个字符的值,您可能需要使用char32_t,因为它没有多字节字符串或代理对,只需转换为unsigned long即可转换为十进制值。我会链接到我写的一个例子,但听起来好像你想自己解决这个问题。

C++ 14直接支撑类型char8_tchar16_tchar32_t,除了传统wchar_t有时意味着UCS-32,有时UTF-16LE,有时UTF-16BE,有时不同的东西。它还允许您在运行时存储字符串,无论您使用前缀u8",u"和前缀以及\uXXXX unicode转义作为回退,以任何格式保存源文件。为了向后兼容,您可以使用十六进制转义码将UTF-8编码在unsigned char的数组中。

因此,您可以以任何您想要的格式存储数据。您也可以使用所有区域设置都必须支持的方面codecvt<wchar_t,char,mbstate_t>。在<wchar.h><uchar.h>中也有多字节字符串函数。

我强烈建议您将所有新的外部数据存储在UTF-8中。这包括你的源文件! (令人烦恼的是,一些较旧的软件仍然不支持它。)在内部使用相同的字符集作为您的库也很方便,这些库在Windows上将为UTF-16(wchar_t)。如果你需要固定长度的字符,可以保存任何代码点而没有特殊情况,那么char32_t将会很方便。

-1

本来计算机是为美国市场设计的,并使用Ascii--美国信息交换代码。这有7位代码,只是基本的英文字母和一些标点符号,以及用于驱动纸张和打印机终端的低端代码。 随着计算机的开发和开始用于语言处理和数字工作一样多,这变得不足。发生的第一件事是提出了8比特的各种扩展。这可以覆盖大部分装饰的欧洲字符(重音符号等),也可以提供一系列基本的图形用于创建菜单和面板,但无法实现这两种效果。仍然没有办法代表希腊语之类的非拉丁字符集。因此提出了一个16位代码,并称为Unicode。微软很早就采用了这个技术,并发明了wchar WCHAR(它有各种标识符)来保存国际字符。然而,它出现了16位不足以保存所有字形的常用情况,而Unicode协会也引入了一些与微软16位代码集不兼容的问题。

所以Unicode可以是一系列的16位整数。这是wchar字符串。 Ascii文本现在在高字节之间有零个字符,所以你不能将一个宽字符串传递给一个函数Expectign Ascii。由于16位几乎不足够,所以还生成了32位unicode集。

然而,当你将unicode保存到一个文件时,这就产生了问题,它是32位的16位吗?它是大端还是小端。因此,在数据开始时提出了一个标志来解决这个问题。问题在于,内存中的文件内容不再与字符串内容匹配。

C++ std:;字符串是模板化的,所以它可以使用基本字符或其中一种宽类型,几乎总是在实践中使用微软的16位近Unicode编码。

UTF-8被发明出来营救。这是一个多字节可变长度编码,它使用ascii只有7位的事实。所以如果高位被设置,这意味着你在字符中有两个,三个或四个字节。现在很多字符串都是英文或主要是人类可读的数字,所以基本上是ascii。这些字符串在Ascii中与UTF-8中的字符串相同,这使得生活变得轻松很多。你没有字节顺序约定问题。你确实有这样的问题,你必须用UTF-8编码来解码UTF-8,而不是完全无关紧要的功能,并且记住按照正确的字节数提前你的读取位置。

UTF-8确实是答案,但其他编码仍在使用中,您会遇到它们。

+0

感谢您的努力马尔科姆。不知道为什么人们不赞成你的答案。如果有人不喜欢他/她应该忽略的东西。这个功能应该被删除( – user18490

相关问题