【问题标题】:c++11 how to convert a hex string into unicode stringc++11如何将十六进制字符串转换为unicode字符串
【发布时间】:2019-03-20 23:34:16
【问题描述】:

似乎即使使用 C++11 这也不是简单的,例如,

string instring = "0x1234";  // hex string
string outstring = "ሴ"       // equivalent to "\u1234"

假设我只有这个十六进制数字作为从其他地方传递下来的输入字符串,这意味着我事先不知道十六进制数字。实现这一目标的最佳方法是什么?

看起来这个 '\u' 前缀仅在紧跟十六进制数字时才有效。

我尝试过但没有成功的想法:

1. std::regex_replace(instring, "0x", "\\u");
2. sprintf();

【问题讨论】:

  • Unicode 定义代码点。您需要一种编码来以某种方式存储代码点。你想使用什么编码?这个问题对我来说似乎有点不清楚。如果您要定义文字,只需使用'\u' 定义它们。如果您正在从文件中读取所需的字符,只需存储它们。如果您正在读取十六进制值的序列,并且想要对它们进行编码以表示实际的代码点,则需要解析它们。你的愿望是什么?
  • 是的,我应该澄清得更好。这确实是最后一种情况,我只从其他地方获取字符串。也更新了问题。
  • 如果明天之前没人回答,我会提供一个算法和一些建议。不过,如果您向我们展示您的尝试,那就太好了。

标签: c++ c++11 unicode


【解决方案1】:

std::regex_replace 将不起作用,因为它使用存储的实际数据。 "\\u" 字符改变文本在数据中的存储方式。简单地更换它是行不通的——为时已晚。

可以玩一点编码。很确定肯定有一个库(我写了一个parser,它接受一个原始的UTF-8文件并输出多字节字符的代码点,但它现在真的受到限制,加上它没有经过适当的测试),但我们可以尝试实现我们自己的有限版本,以了解里面发生了什么。

首先,为什么是 UTF-8?真的没有很好的理由不这样做。这取决于您的local encoding,但您可以根据需要进行调整。

使用 UTF-8 编辑器编写的代码示例:

#include <iostream>
#include <string>
#include <bitset>

int main() {
    std::string str = "\u1234";
    for(char c : str) {
        std::cout << std::bitset<8>(static_cast<uint8_t>(c)) << ' ';
    }
}

将产生11100001 10001000 10110100

您可以阅读有关std::bitset here 的信息。如果您对结果感到惊讶(您不熟悉 UTF-8 的工作原理),我鼓励您watch 一个很棒的视频。


回到主题。目标是计算 hex 值,将其转换为 UTF-8 字节序列(characters)并将其存储到std::string

伪代码:

string := input;
hex := convertToHex(string);
sequence := hexToUTF8(hex);
output := seqToString(sequence); 

我们假设input 将作为十六进制字符串提供。要将十六进制字符串转换为十进制值,我们可以使用std::stringstream

std::string input = "0x1234";
std::stringstream stream{};
stream << std::hex << input;
int val;
stream >> val;

val 将等于 4660

现在,我们需要在给定该值的情况下构造一个 UTF-8 字节序列。我们可以使用现有的库std::wstring_convert,但注意,从C++17 开始,它被认为已弃用。您最好使用different library,但我们将坚持使用这个来演示示例:

#include <string>
#include <locale>
#include <codecvt>

int main()
{
    int val = 4660; // 0x1234
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
    std::string u8str = converter.to_bytes(val);
}

u8str 现在等于 "\u1234"


完整示例:

#include <cassert>
#include <codecvt>
#include <iostream>
#include <locale>
#include <sstream>
#include <string>

int hex_value(const std::string_view str) {
    std::stringstream stream{};
    stream << std::hex << str;
    int parsed;
    stream >> parsed;
    return parsed;
}

std::string map_to_utf8(const int val) {
    std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
    return converter.to_bytes(static_cast<char32_t>(val));
}

int main() {
    std::string input = "0x1234"; // read from file, let's assume...

    const auto value = hex_value(input);
    const auto str   = map_to_utf8(value);

    using namespace std::literals;
    assert(str == "\u1234"s && "\u1234"s == "ሴ"s); // passes
}

【讨论】:

  • 感谢您详尽的示例和解释!这真的很有帮助!我错过的是 unicode 渲染片,所以代码 sn-p 和外部链接真的很有帮助!一件小事是我需要从 "u1234"s' 中删除 's' 以使其编译。
  • 您知道在 C++17 及更高版本中是否会有更好的 unicode 字符串支持,尤其是当 std::wstring_convert 将被弃用时?我的意思是对 C++ 标准和 STL 的原生支持。
  • @galactica,很高兴我的解决方案对您有所帮助!如果它解决了您的问题,请考虑通过单击分数下方的绿色勾号来接受它,这样未来的访问者就会知道这确实解决了最初的问题。关于""s - 它是一个字符串文字运算符,可从C++14 获得。你可以阅读更多关于它的信息here(推荐)。至于对各种编码的原生支持,我认为我们已经有一段时间不走运了。很遗憾,C++20 的功能列表已经完成,并且没有包含与我们的问题相关的任何内容。
  • 感谢您提供更多详细信息!太糟糕了,我们还没有在 STL 中完全解决这个问题。至于那个 "" 的格式,在 cmets 中可能值得一提的是,它在 C++14 及更高版本中可用。
猜你喜欢
  • 2018-01-31
  • 2013-02-07
  • 2014-12-22
  • 1970-01-01
  • 1970-01-01
  • 2017-02-23
  • 2014-08-03
  • 2022-07-22
  • 2014-12-04
相关资源
最近更新 更多