【问题标题】:Convert C++ std::string to UTF-16-LE encoded string将 C++ std::string 转换为 UTF-16-LE 编码字符串
【发布时间】:2019-03-13 04:55:21
【问题描述】:

我今天已经搜索了几个小时,但找不到任何适合我的东西。我刚刚看过的那个是“How to convert UTF-8 encoded std::string to UTF-16 std::string”。

我的问题是,简单解释一下:

我想在标准 C++ 中创建一个有效的 NTLM 哈希,并且我正在使用 OpenSSL 的库通过其 MD4 例程创建哈希。我知道该怎么做,那么有谁知道如何将std::string 转换为 UTF-16 LE 编码的字符串,我可以将其传递给 MD4 函数以获得正确的摘要?

那么,我可以拥有一个包含char 类型的std::string,并将其转换为UTF16-LE 编码的可变长度std::string_type 吗?是std::u16string,还是std::wstring

我会使用s.c_str()s.data() 吗?length() 函数会在这两种情况下正确报告吗?

【问题讨论】:

  • 你的标题问题很清楚,你的问题主体不是。您是否知道 UTF-16 仍然是可变长度的?你会在std::u16string 中保存一个UTF-16 字符串,而不是std::string? -- 你能关注这个问题吗?现在有点到处都是。
  • 谢谢 DevSolar。你说的对。现在是深夜,我有点沮丧,所以结果有点乱。我知道 UTF16 是可变长度的,所以我正在寻找 std::string 到 std::u16string 或 std::wstring (如果可行的话)。我认为更好的问题可能是:我可以拥有一个包含 char 类型的 std::string,并将其转换为 UTF16-LE 编码的可变长度 std::string_type 吗?无论是 std::u16string 还是 std::wstring。
  • 关于最后一个问题,length() 总是会正确返回字符串对象中 char 类型元素的数量 -- charstd::stringchar16_tstd::u16string,@ 987654337@ 为std::wstring。当然,这些(必然)都不等于代码单元/代码点的数量。 ;-)
  • 必须通过这些步骤 utf8 -> mono-entity-unicode -> utf16 。不知道代码点,你不可能从 8 到 16。

标签: c++ utf-8 character-encoding utf-16 stdstring


【解决方案1】:

我认为这样的事情应该可以解决问题:

std::string utf16_to_utf8(std::u16string const& s)
{
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff,
        std::codecvt_mode::little_endian>, char16_t> cnv;
    std::string utf8 = cnv.to_bytes(s);
    if(cnv.converted() < s.size())
        throw std::runtime_error("incomplete conversion");
    return utf8;
}

std::u16string utf8_to_utf16(std::string const& utf8)
{
    std::wstring_convert<std::codecvt_utf8_utf16<char16_t, 0x10ffff,
        std::codecvt_mode::little_endian>, char16_t> cnv;
    std::u16string s = cnv.from_bytes(utf8);
    if(cnv.converted() < utf8.size())
        throw std::runtime_error("incomplete conversion");
    return s;
}

注意: std::wstring_convertC++17已弃用,但我仍然喜欢使用它而不是给定的非标准库它是可移植的,没有依赖关系,并且在被替换之前无疑会一直存在。

而且,如果所有其他方法都失败了,您可以使用替代代码重新实现这些相同的功能,而无需更改应用程序的任何其他部分。

【讨论】:

  • 您好 Galik,非常感谢您抽出宝贵时间输入此内容。我尝试了几个小时,我用谷歌搜索,我发疯了......没有用,尽管一切都告诉我这看起来很理想,而且我们在正确的轨道上。不过老实说,我并不完全了解 codecvt 或任何转换的 C++ 文档。我更像是一个喜欢尽可能使用 C++ 特性的 C 程序员。我同意赞成使用它而不是非标准库。应该可以的。
  • @JYG 在我的系统上,这会从UTF-8 生成UTF-16LE 编码。我在x86 CPU 上运行,这是littleendian。您是否在 bigendian 系统上运行?
  • @JYG 我更改了代码以明确指定UTF-16le,这样可以解决问题吗?
【解决方案2】:

道歉,第一手资料...这将是一个带有一些长代码的丑陋回复。我最终使用了以下函数,同时有效地将iconv逐个文件编译到我的Windows应用程序文件中:)

希望这会有所帮助。

char* conver(const char* in, size_t in_len, size_t* used_len)
{
    const int CC_MUL = 2; // 16 bit
    setlocale(LC_ALL, "");
    char* t1 = setlocale(LC_CTYPE, "");
    char* locn = (char*)calloc(strlen(t1) + 1, sizeof(char));
    if(locn == NULL)
    {
        return 0;
    }

    strcpy(locn, t1);
    const char* enc = strchr(locn, '.') + 1;

#if _WINDOWS
    std::string win = "WINDOWS-";
    win += enc;
    enc = win.c_str();
#endif

    iconv_t foo = iconv_open("UTF-16LE", enc);

    if(foo == (void*)-1)
    {
        if (errno == EINVAL)
        {
            fprintf(stderr, "Conversion from %s is not supported\n", enc);
        }
        else
        {
            fprintf(stderr, "Initialization failure:\n");
        }
        free(locn);
        return 0;
    }

    size_t out_len = CC_MUL * in_len;
    size_t saved_in_len = in_len;
    iconv(foo, NULL, NULL, NULL, NULL);
    char* converted = (char*)calloc(out_len, sizeof(char));
    char *converted_start = converted;
    char* t = const_cast<char*>(in);
    int ret = iconv(foo,
                    &t,
                    &in_len,
                    &converted,
                    &out_len);
    iconv_close(foo);
    *used_len = CC_MUL * saved_in_len - out_len;

    if(ret == -1)
    {
        switch(errno)
        {
        case EILSEQ:
            fprintf(stderr,  "EILSEQ\n");
            break;
        case EINVAL:
            fprintf(stderr,  "EINVAL\n");
            break;
        }

        perror("iconv");
        free(locn);
        return 0;
    }
    else
    {
        free(locn);
        return converted_start;
    }
}

【讨论】:

  • 链接到iconv 加上必要的包含也会改进这个答案。
  • 谢谢fritzone!我一直在努力让 iconv() 工作好几个小时,直到我放弃并回来再看一遍:) 非常感谢,现在每次正确转换后 ntlm 哈希都是正确的。谁在乎它是否不是“很棒”的代码,它可以工作!
  • @DevSolar 这只是我在一个非常古老的实验项目中实现的一个功能......不幸的是,它没有很好的评论,因为它属于家庭种植的宠物项目....所以我有点忘了什么和为什么,我只知道它很好......有效。
  • 您好 DevSolar,我刚刚复制并粘贴到上面的 main() 中,并将内联添加到函数签名中。要使用它,#include 并像这样调用它:
  • 您好 DevSolar,我刚刚复制并粘贴到上面的 main() 中,并将内联添加到函数签名中。要使用它,#include 并像这样调用它:char pass[64]; strcpy(通过,“p4ssw0rd”); size_t used_bytes = 64*3; char unicode_password = conver(pass, strlen(pass), &used_bytes); / 现在做一个 NTLM 哈希 */ MD4_CTX ctx; MD4_Init(&ctx); MD4_Update(&ctx, unicode_password, used_bytes); MD4_Final(message_digest_somewhere, &ctx);安装libiconv,编译:g++ -o program program.cpp -lcrypto -liconv,我也在那里添加了openssl函数的lib。也是免费的(unicode_password)。
猜你喜欢
  • 2013-09-26
  • 2012-06-20
  • 1970-01-01
  • 2011-11-01
  • 1970-01-01
  • 2011-09-06
  • 2014-02-05
  • 2014-01-18
  • 2012-03-30
相关资源
最近更新 更多