使用非 ascii 数据将 std::string 写入文件
Writing std::string with non-ascii data to file
下面是我的问题的一个简化示例。我有一些外部字节数据,它似乎是一个带有 cp1252 编码度数符号 0xb0
的字符串。当它作为 std::string
存储在我的程序中时,它被正确地表示为 0xffffffb0
。但是,当将该字符串写入文件时,生成的文件只有一个字节长,只有 0xb0
。如何将字符串写入文件? UTF-8
的概念是如何产生的?
#include <iostream>
#include <fstream>
typedef struct
{
char n[40];
} mystruct;
static void dump(const std::string& name)
{
std::cout << "It is '" << name << "'" << std::endl;
const char *p = name.data();
for (size_t i=0; i<name.size(); i++)
{
printf("0x%02x ", p[i]);
}
std::cout << std::endl;
}
int main()
{
const unsigned char raw_bytes[] = { 0xb0, 0x00};
mystruct foo;
foo = *(mystruct *)raw_bytes;
std::string name = std::string(foo.n);
dump(name);
std::ofstream my_out("/tmp/out.bin", std::ios::out | std::ios::binary);
my_out << name;
my_out.close();
return 0;
}
运行 上述程序在 STDOUT
上产生以下内容
It is '�'
0xffffffb0
首先,这是一篇必读的文章:
现在,完成后,您必须了解什么类型代表 p[i]
。
是char
,在C语言中是一个带sign
的小整数值! char
可以为负!
现在,由于您有 cp1252
个字符,它们不在 ASCII 的范围内。这意味着这些字符被视为负值!
现在,当它们转换为 int
时,符号位被复制,当您尝试打印它时,您将看到 0xffffff<actual byte value>
。
要在 C
中处理该问题,首先您应该转换为 unsigned char
:
printf("0x%02x ", (unsigned char)p[i]);
然后默认转换将用零填充缺失的位,printf()
将为您提供正确的值。
现在,在 C++ 中,这有点令人讨厌,因为 char
和 unsigned char
被流运算符视为字符表示。所以要以十六进制方式打印它们,应该是这样的:
int charToInt(char ch)
{
return static_cast<int>(static_cast<unsigned char>(ch));
}
std::cout << std::hex << charToInt(s[i]);
现在,从 char
到 unsigned int
的直接转换将无法解决问题,因为编译器会静默地首先执行与 int
的对话。
看这里:https://wandbox.org/permlink/sRmh8hZd78Oar7nF
UTF-8 与此问题无关。
题外话:请大家在编写纯C++代码时,不要使用C
。它毫无意义,使代码更难维护,而且速度也不快。所以:
- 不要使用
char*
或char[]
来存储字符串。只需使用 std::string
.
- 不要使用
printf()
,请使用 std::cout
(或 fmt
库,如果你喜欢格式字符串——它将成为未来的 C++ 标准)。
- 不要使用
alloc()
、malloc()
、free()
- 在现代 C++ 中,使用 std::make_unique()
和 std::make_shared()
。
下面是我的问题的一个简化示例。我有一些外部字节数据,它似乎是一个带有 cp1252 编码度数符号 0xb0
的字符串。当它作为 std::string
存储在我的程序中时,它被正确地表示为 0xffffffb0
。但是,当将该字符串写入文件时,生成的文件只有一个字节长,只有 0xb0
。如何将字符串写入文件? UTF-8
的概念是如何产生的?
#include <iostream>
#include <fstream>
typedef struct
{
char n[40];
} mystruct;
static void dump(const std::string& name)
{
std::cout << "It is '" << name << "'" << std::endl;
const char *p = name.data();
for (size_t i=0; i<name.size(); i++)
{
printf("0x%02x ", p[i]);
}
std::cout << std::endl;
}
int main()
{
const unsigned char raw_bytes[] = { 0xb0, 0x00};
mystruct foo;
foo = *(mystruct *)raw_bytes;
std::string name = std::string(foo.n);
dump(name);
std::ofstream my_out("/tmp/out.bin", std::ios::out | std::ios::binary);
my_out << name;
my_out.close();
return 0;
}
运行 上述程序在 STDOUT
上产生以下内容It is '�'
0xffffffb0
首先,这是一篇必读的文章:
现在,完成后,您必须了解什么类型代表 p[i]
。
是char
,在C语言中是一个带sign
的小整数值! char
可以为负!
现在,由于您有 cp1252
个字符,它们不在 ASCII 的范围内。这意味着这些字符被视为负值!
现在,当它们转换为 int
时,符号位被复制,当您尝试打印它时,您将看到 0xffffff<actual byte value>
。
要在 C
中处理该问题,首先您应该转换为 unsigned char
:
printf("0x%02x ", (unsigned char)p[i]);
然后默认转换将用零填充缺失的位,printf()
将为您提供正确的值。
现在,在 C++ 中,这有点令人讨厌,因为 char
和 unsigned char
被流运算符视为字符表示。所以要以十六进制方式打印它们,应该是这样的:
int charToInt(char ch)
{
return static_cast<int>(static_cast<unsigned char>(ch));
}
std::cout << std::hex << charToInt(s[i]);
现在,从 char
到 unsigned int
的直接转换将无法解决问题,因为编译器会静默地首先执行与 int
的对话。
看这里:https://wandbox.org/permlink/sRmh8hZd78Oar7nF
UTF-8 与此问题无关。
题外话:请大家在编写纯C++代码时,不要使用C
。它毫无意义,使代码更难维护,而且速度也不快。所以:
- 不要使用
char*
或char[]
来存储字符串。只需使用std::string
. - 不要使用
printf()
,请使用std::cout
(或fmt
库,如果你喜欢格式字符串——它将成为未来的 C++ 标准)。 - 不要使用
alloc()
、malloc()
、free()
- 在现代 C++ 中,使用std::make_unique()
和std::make_shared()
。