【问题标题】:Writing std::string with non-ascii data to file将带有非 ascii 数据的 std::string 写入文件
【发布时间】:2019-12-11 07:17:53
【问题描述】:

以下是我的问题的简化示例。我有一些外部字节数据,它似乎是一个带有 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 

【问题讨论】:

  • 如果实际字节为 0xb0,您期望什么?忘记 ansi 字符串并在应用程序内使用 unicode,序列化使用 UTF-8。
  • std::string name = std::string(foo.n); -- 这不会构造一个包含两个字符的字符串。
  • 顺便说一句,在 C++ 中,typedef 不需要 struct
  • 1. *(mystruct *)raw_bytes 不合法,任何事情都可能发生。 2. 0xffffffb00xb0 char 值转换为int。它与 ASCII、cp1252 或任何此类性质的东西无关。
  • @MichaelChourdakis - 您能否说明我将如何将包含非 ascii 字节 0xb0 的外部数据转换为 unicode 字符串?

标签: c++ string utf-8 non-ascii-characters utf


【解决方案1】:

首先,这是必读的:

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&lt;actual byte value&gt;

要在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]);

现在,从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 编码的内容转换为 UTF-8。必须有一个 c++ 通用的方法来做到这一点,但我找不到它
  • 我找到了适合我的解决方案。在我的原始代码中,而不是 std::string name = std::string(foo.n); 我现在正在做 std::string name = boost::locale::conv::to_utf&lt;char&gt;(foo.n, "Latin1");
猜你喜欢
  • 1970-01-01
  • 2013-03-01
  • 2011-02-05
  • 1970-01-01
  • 2014-04-04
相关资源
最近更新 更多