在std :: u8string和std :: string之间转换

时间:2019-04-07 06:21:01

标签: c++ unicode utf-8 c++20

C ++ 20为UTF-8添加了char8_tstd::u8string。但是,没有std::cout的UTF-8版本,并且OS API通常期望char和执行字符集。因此,我们仍然需要一种在UTF-8和执行字符集之间进行转换的方法。

我正在重读char8_t paper,看来在UTF-8和ECS之间转换的唯一方法是使用std::c8rtombstd::mbrtoc8函数。但是,它们的API极为混乱。有人可以提供示例代码吗?

7 个答案:

答案 0 :(得分:1)

在C ++ 20中,UTF-8“支持”似乎是个坏笑话。

STL中唯一的UTF功能是支持字符串和string_views(std :: u8string,std :: u8string_vew,std :: u16string等)。就这些。对于正则表达式,格式,文件I / O等中的UTF编码,不提供STL支持。

在C ++ 17中,您可以-至少-轻松地将任何UTF-8数据视为'char'数据,从而可以使用std :: regex,std :: fstream,std :: cout等。不会损失性能。

在C ++ 20中,情况将会改变。例如,您不能再写std::string text = u8"...";,将不可能写出类似的内容

std::u8fstream file; std::u8string line; ... file << line;

因为它不是std :: u8fstream。

即使新的C ++ 20 std :: format根本不支持UTF,因为所有必需的重载都被丢失了。你不会写

std::u8string text = std::format(u8"...{}...", 42);

更糟糕的是,在std :: string和std :: u8string之间(甚至在const char *和const char8_t *之间)进行简单的转换(或转换)是不容易的。因此,如果要格式化(使用std :: format)或输入/输出(std :: cin,std :: cout,std :: fstream等),您必须在内部复制所有字符串。 -那将是不必要的性能杀手。

最后,如果没有输入,输出和格式,UTF将有什么用途?

答案 1 :(得分:1)

VS 2019

  ostream& operator<<(ostream& os, const u8string& str)
    {
        os << reinterpret_cast<const char*>(str.data());
        return os;
    }

要将控制台设置为 UTF-8,请使用 https://github.com/MicrosoftDocs/cpp-docs/issues/1915#issuecomment-589644386

答案 2 :(得分:0)

目前,std::c8rtombstd::mbrtoc8是该标准提供的仅有的使执行编码和UTF-8之间转换的接口。接口很尴尬。它们旨在匹配std::c16rtombstd::mbrtoc16之类的现有接口。在C ++标准中为这些新接口添加的措辞有意与在C标准中针对预先存在的相关功能的措辞相匹配(希望这些新功能最终会被添加到C中;我仍然需要继续这样做)。与C标准措辞相匹配的目的,是要使之困惑,这是为了确保熟悉C措辞的任何人都认识到char8_t接口的工作方式相同。

cppreference.com提供了这些函数的UTF-16版本的一些示例,这些示例对于理解char8_t变体应该很有用。

答案 3 :(得分:0)

当前(2019年11月)尚未提供“ {3”制作的未来C ++ 20就绪的编译器提供的std::c8rtombstd::mbrtoc8,以实现执行编码之间的转换和UTF-8。它们在C ++ 20标准中进行了描述。

对我来说,这可能是主观的,但是c8rtomb()并不是一个“尴尬”的界面。

WANDBOX

//  g++ prog.cc -std=gnu++2a
//  clang++ prog.cc -std=c++2a
#include <stdio.h>
#include <clocale>
#ifndef __clang__
#include <cuchar>
#else
// clang has no <cuchar>
#include <uchar.h>
#endif
#include <climits>

template<typename C32, size_t N>
void  u32sample( const C32 (&str32)[N] )
{
    #ifndef __clang__
    std::mbstate_t state{};
    #else
    mbstate_t state{};
    #endif

    char out[MB_LEN_MAX]{};
    for(char32_t const & c : str32)
    {
    #ifndef __clang__
        /*std::size_t rc =*/ std::c32rtomb(out, c, &state);
    #else
        /* std::size_t rc =*/ ::c32rtomb(out, c, &state);
    #endif
        printf("%s", out ) ;
    }
}

template<typename C8, size_t N>
void  u8sample( const C8 (& str8)[N])
{
    #ifndef __clang__
    std::mbstate_t state{};
    #else
    mbstate_t state{};
    #endif

    char out[MB_LEN_MAX]{};
    for(char8_t const & c : str8)
    {
    #ifndef __clang__
       // not in std for gcc 10, C++20
       /* std::size_t rc = */ /* std::c8rtomb(out, c, &state); */
       printf("%s", "std::c8rtomb() is not implemented, " __DATE__ ) ;
       break;
    #else
       // not there for clang 10, C++20
       /* std::size_t rc = */ /* ::c8rtomb(out, c, &state); */
        printf("%s", "::c8rtomb() is not implemented, " __DATE__ ) ;
       break;
    #endif
        printf("%s", out ) ;
    }
}

int main () {
    std::setlocale(LC_ALL, "en_US.utf8");

    #ifdef __linux__
    printf("\nLinux like OS, ") ;
    #endif

    #ifdef __clang__
    printf(" clang %d.%d.%d\n", __clang_major__ , __clang_minor__ , __clang_patchlevel__   ) ;
    #else
    printf(" gcc %d.%d.%d\n", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ ) ;
    #endif

   printf("\nchar32_t *, Converting to 'char *', and then printing\n" ) ;
   u32sample( U"ひらがな" ) ;
   printf("\nDone ...\n" ) ;

   printf("\nchar8_t *, Converting to 'char *', and then printing\n" ) ;
   u8sample( u8"ひらがな" ) ;
   printf("\nDone ..." ) ;

    return 42;
}

我已经注释掉并记录下来,直到今天仍未编译的行。

答案 4 :(得分:0)

C ++权威机构在年度CppCon大会(例如在2018年和2019年)上给出的常见答案是,您应该选择自己的UTF8库来这样做。有各种各样的口味,只需选择您喜欢的一种即可。在C ++方面,对unicode的理解和支持仍然很少。

有些人会在C ++ 23中实现某些功能,但到目前为止我们还没有正式的工作组。

答案 5 :(得分:0)

以下是应符合C ++ 20的代码。由于目前没有编译器(2020年3月)实现本文中定义的转换函数,因此我决定不限制自己使用当前实现的功能,并使用C ++ 20的完整规范。因此,我采用的是代码单元范围,而不是采用import { writable, get } from 'svelte/store' const signUp = writable() const signUpStore = { subscribe: signUp.subscribe, setSignUp: (items) => { signUp.set(items) // console.log('items : ', items, signUp) }, addSignUp: (data) => { signUp.update(items => { return items.concat(data) }) }, getSignUp: () => { get(signUp) } } export default signUpStore; std::basic_string。返回值的通用性较差,但将其更改为采用输出范围并不容易。这留给读者练习。

std::basic_string_view

答案 6 :(得分:0)

AFAIK C++ 还没有为这种转换提供工具。但是,我首先建议使用 std::u8string,因为它在标准中的支持很差,并且根本不受任何系统 API 支持(并且可能永远不会因为兼容性原因而支持)。在大多数平台上,正常的 char 字符串已经是 UTF-8,而在带有 MSVC 的 Windows 上,您可以使用 /utf-8 进行编译,这将为您在主要操作系统上提供可移植的 Unicode 支持。