将std :: string从一个语言环境转换为另一个语言环境

时间:2014-11-20 10:41:22

标签: c++ string character-encoding localization locale

如何将一个区域设置的std::string转换为另一个区域设置? 例如,我在std::string中有std::locale("ru_RU.koi8r")以及在std::stringstd::locale("ru_RU.CP1251")或其他任何内容中创建std::locale("zh_CN.gb18030")的内容。

1 个答案:

答案 0 :(得分:0)

是的,codecvt是解决此类问题的正确答案。

codecvt简介:

codecvt<internal, external, state>方面的目标是提供内部表示(内存中的变量)和流表示(文件中的数据)之间的流字符编码转换:

  • 用于流的codecvt(如果有)在其语言环境中使用imbue()定义。
  • 从流中读取数据时,转换是从外部(文件缓冲区)通过方面的函数do_in()进行内部编码。
  • 在将数据写入流时,通过函数do_out()从内部数据到外部(存储在文件中)进行转换。
  • 转换可以在具有或不具有状态的单字节或多字节编码之间(如果其中一个编码具有可变长度,则需要)。

我在文件中的代码页850格式和内存中的iso8859-1之间进行转换的非常简单的示例。当然,你可以调整它来为koi8r和CP1251做同样的事情。

原则如下:
enter image description here

由于我的示例中的字符集都是针对类型char定义的,因此我创建了自己的字符集,如下所示:

class codecvt_cp850_iso8859 : public codecvt < char, char, mbstate_t > {
public:
    explicit codecvt_cp850_iso8859(size_t r=0) : codecvt(r) {}
protected:
    result do_in(mbstate_t &s, const char *from, const char *from_end, 
        const char*&from_next, 
        char *to, char*to_end, char*&to_next) const  // from cp850 to iso8859-1
    {
        result res=partial; 
        const char*p;
        for (p = from; p != from_end && to != to_end; p++)
            *to++ = ... /*  your stuff here, for example a conversion table */
        from_next = p;  // what's the next char to convert
        to_next = to;   // what's the next place to convert to
        if (p == from_end)
            res = p == from ? noconv : ok;
        return res; 
    }

    result do_out(mbstate_t &s, const char *from, const char *from_end, const char*&from_next,
        char *to, char*to_end, char*&to_next) const  // from iso8859-1 to cp850
    {
        result res = partial;
        const char*p;
        for (p = from; p != from_end && to != to_end; p++)
            *to++ = ... /* conversion in the other direction */ 
        from_next = p;  to_next = to;
        if (p == from_end)
                res = p == from ? noconv : ok;
        return res;
    }
    result do_unshift(mbstate_t &s, char*to, char*to_end, char*& to_next) const {
        to_next = to;    // Attention MSVC segfaults if this is missing ! 
        return ok;
    }
    int encoding() const {  return 1; } // fixed length for external representation
    bool do_always_noconv() const { return false; }

然后您可以使用此类代码:

// read with conversion from cp850 in stream to iso in memory 
ifstream is850("test-cp850.txt");
is850.imbue(locale(is850.getloc(), new codecvt_cp850_iso8859 ())); // use our conversion
getline(is850, isotext); 
... 
// writing an iso string to a CP850 file: 
ofstream os850("test-cp850fromiso.txt");
os850.imbue(locale(os850.getloc(), new codecvt_cp850_iso8859()));
os850 << myisotext << endl;
...

请注意,标准库对提供的codect拥有所有权,并在不再需要时将其重新关联。

应用于字符串

最后,您还可以使用codecvt在内存中的两种格式之间转换字符串。

如下面的评论所示,字符串本身没有区域设置。

所以一种方法是使用stringstreams。另一种方法是直接使用codecvt's functionsmycodecvt::in()mycodecvt::out()mycodecvt::length()

最后一种方法是使用wstring_convert。但是这个具有要求具有宽字符的转换的不便之处。