使用非 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 

首先,这是一篇必读的文章:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

现在,完成后,您必须了解什么类型代表 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++ 中,这有点令人讨厌,因为 charunsigned char 被流运算符视为字符表示。所以要以十六进制方式打印它们,应该是这样的:

int charToInt(char ch) 
{
    return static_cast<int>(static_cast<unsigned char>(ch));
}

std::cout << std::hex << charToInt(s[i]);

现在,从 charunsigned 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()