将内存中的 16 位转换为 std::string
Posted
技术标签:
【中文标题】将内存中的 16 位转换为 std::string【英文标题】:Convert 16 bits in memory into std::string 【发布时间】:2013-07-29 16:26:15 【问题描述】:我从内存中的结构中获取 16 位,我需要将它们转换为字符串。 16位代表一个unicode字符:
typedef struct my_struct
unsigned unicode : 16;
my_struct;
我首先将这些位转换为一个无符号字符,它适用于小到足以容纳一个字符的值。但是,对于像“♪”这样的字符,它会错误地截断。这是我目前所拥有的:
char buffer[2] = 0 ;
wchar_t wc[1] = 0 ;
wc[0] = page->text[index].unicode;
std::cout << wc[0] << std::endl; //PRINT LINE 1
int ret = wcstombs(buffer, wc, sizeof(buffer));
if(ret < 0)
printf("SOMETHING WENT WRONG \n");
std::string my_string(buffer);
printf("%s \n", my_string.c_str()); //PRINT LINE 2
打印第 1 行当前打印:“9834”,打印第 2 行打印:“”(空字符串)。我试图让 my_string 包含 '♪'。
【问题讨论】:
你不能将 16 位放入 8 位而不丢失任何东西。您的选择是从(显然)UTF-16 转换为 UTF-8(使用多个 8 位字符来保存一个 16 位代码单元)或将其保留为 UTF-16(例如,std::wstring
保存单位为 @987654325 @,可能是 UTF-16)。如果这些都不起作用,您可以直接在您的my_struct
上实例化std::basic_string
:std::basic_string<my_struct> whatever;
你不能把 16 磅面粉放在一个 8 磅的袋子里。
@Jerry Coffin:有点迂腐,但 std::*string 不存储(或关心)字符编码。即使 wchar_t 是 16 位的,它也可能是 UCS-2。通常,您需要 UCS-4 或 UTF-8。 UTF-16 结合了两者的缺点,没有任何好处。
@DanielKO:我当然不会推荐 UTF-16 作为一般规则——这只是反映了 OP 对 16 位的使用。 UCS-2 obsolete 已经很长时间了。
@mirandak:除非库真的很旧(并且在过去十年左右没有更新),否则它可能是 UTF-16 而不是 UCS-2。
【参考方案1】:
请阅读一下“字符编码”的含义,例如:What is character encoding and why should I bother with it
然后弄清楚您输入的是什么编码,以及您需要在输出中使用什么编码。这意味着要弄清楚您的文件格式/GUI 库/控制台期望什么。
然后使用像 libiconv 这样可靠的东西在它们之间进行转换,而不是使用如此实现定义的几乎无用的 wcstombs()+wchar_t。
例如,您可能会发现您的输入是 UCS-2,您需要将其输出为 UTF-8。我的系统有 32 位 wchar_t,我不会指望它从 UCS-2 转换为 UTF-8。
【讨论】:
【参考方案2】:要将 UTF-16 转换为 UTF-8,请使用 codecvt_utf8<char16_t>
:
#include <iostream>
#include <string>
#include <locale>
#include <codecvt>
int main()
char16_t wstr16[2] = 0x266A, 0;
auto conv = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>;
auto u8str = std::stringconv.to_bytes(wstr16);
std::cout << u8str << '\n';
【讨论】:
auto u8str = std::string conv.to_bytes( wstr16 ) ;
的意义何在,而不是 std::string u8str( conv.to_bytes( wstr16 ) );
,除了可能混淆?
@JamesKanze 这是 AAA 风格:herbsutter.com/2013/06/13/…
@mirandak 您在评论 std::string s("\u266A");
中使用了嵌入式 Unicode 代码点,这是 C++11 的一项功能。
@ecatmur 另一个反模式。如果您不想命名类型,请使用 Python。但除了少数特殊情况外,您确实想要命名类型,以便读者对发生的事情有所了解。 AAA 只是糟糕的工程。
@ecatmur 它们在我的 C++98 标准副本中(在 C90 中也是如此)。【参考方案3】:
如果我正确完成了转换,UTF-16 中的 0x9834(16 位
Unicode) 转换为三个字节序列 0xE9、0xA0、
UTF-8(8 位 Unicode)中的 0xB4。不知道其他窄
字节编码,但我怀疑任何会短于 2 个字节。
您将两个字节的缓冲区传递给wcstombs
,这意味着
返回的最多 1 个字节的字符串。 wcstombs
停止
翻译(没有失败!)当没有更多空间时
目标缓冲区。您也未能L'\0'
终止
输入缓冲区。目前这不是问题,因为
wcstombs
将在它到达那里之前停止翻译,但你
通常应该添加额外的L'\0'
。
那该怎么办:
首先,最重要的是,在调试此类事情时,请查看
wcstombs
的返回值。我敢打赌它是0
,因为
空间不足。
其次,我会给自己一点余地。合法的统一码
最多可以在 UTF-8 中产生四个字节,所以我会分配
输出至少 5 个字节(不要忘记结尾的 '\0'
)。
同样,您需要一个尾随L'\0'
作为输入。
所以:
char buffer[ 5 ];
wchar_t wc[] = page->text[index].unicode, L'\0' ;
int ret = wcstombs( buffer, wc, sizeof( buffer ) );
if ( ret < 1 ) // And *not* 0
std::cerr << "OOPS\n";
std::string str( buffer, buffer + ret );
std::cout << str << '\n';
当然,毕竟,还有一个问题是什么
(最终)显示设备使用 UTF-8(或任何
多字节窄字符编码是---UTF-8差不多
在 Unix 下通用,但我不确定 Windows。)但是
既然你说显示"\u9834"
似乎有效,它
应该没问题。
【讨论】:
Windows 控制台理论上可以显示 UTf-8,但要真正做到这一点很棘手。 我知道你无法窥视我的计算机,但是一旦出现值 > 127 的字符,wcstombs 就会返回 -1。 编辑: 错误不是字符,但你知道我的意思 认为这是一个语言环境问题,因为我打了 "setlocale(LC_ALL, "");"在那里,它突然起作用了!现在要弄清楚我真正需要什么语言环境...但是谢谢!!! 问题中的 9834 值似乎是十进制的。显示的音符是 U+266A(恰好是 9834 的十六进制)。 @mirandak 是的。wcstombs
对区域设置敏感,并且可能不在默认 "C"
区域设置中翻译大于 127 的字符。我应该提到这一点。 (但事实上你没有提到从中得到错误,并且你可以显示"\9834"
让我相信你已经解决了这些方面。)以上是关于将内存中的 16 位转换为 std::string的主要内容,如果未能解决你的问题,请参考以下文章