函数setlocale有什么作用?

时间:2019-01-23 03:13:30

标签: c++

我编写了一个将wstring转换为字符串的函数。如果删除代码setlocale(LC_CTYPE,“”),程序将出错。我参考cplusplus阅读文档。

  

包含C语言环境名称的C字符串。这些是系统特定的   但至少必须存在以下两个语言环境:

     

“ C”最小的“ C”语言环境
  “”环境的默认语言环境

     

如果此参数的值为NULL,则该函数不执行任何操作   更改为当前语言环境,但当前语言环境的名称为   仍由该函数返回。

我的代码在这里,来自cplusplus.com的源代码(我添加了一些汉字):

/* wcstombs example */
#include <stdio.h>      /* printf */
#include <stdlib.h>     /* wcstombs, wchar_t(C) */
#include <locale.h>     /* setlocale */
int main() 
{
    setlocale(LC_CTYPE, "");
    const wchar_t str[] = L"中国、wcstombs example";
    char buffer[64];
    int ret;

    printf ("wchar_t string: %ls \n",str);

    ret = wcstombs ( buffer, str, sizeof(buffer) );
    if (ret==64) 
        buffer[63]='\0';
    if (ret) 
        printf ("length:%d,multibyte string: %s \n",ret,buffer);

  return 0;
}

如果删除代码setlocale(LC_CTYPE,“”),则程序无法按预期运行。 我的问题是:“如果我在不同的计算机上运行,​​程序会有所不同吗?如文档所说,如果语言环境为”,函数不会对当前语言环境进行任何更改,但仍会返回当前语言环境的名称通过功能。” 因为不同机器上的当前语言环境可能不同?

这是我的c ++版本的将wstring转换为string的c ++版本,而string到wstring不需要函数setlocale,并且程序运行良好:

/*
    string converts to wstring
*/
std::wstring s2ws(const std::string& src)     
{  
    std::wstring res = L"";
    size_t const wcs_len = mbstowcs(NULL, src.c_str(), 0);
    std::vector<wchar_t> buffer(wcs_len + 1);
    mbstowcs(&buffer[0], src.c_str(), src.size());
    res.assign(buffer.begin(), buffer.end() - 1);

    return res;
}  

/*
    wstring converts to string
*/
std::string ws2s(const std::wstring & src)
{ 
   setlocale(LC_CTYPE, "");

   std::string res = "";

   size_t const mbs_len = wcstombs(NULL, src.c_str(), 0);

   std::vector<char> buffer(mbs_len + 1);

   wcstombs(&buffer[0], src.c_str(), buffer.size());

   res.assign(buffer.begin(), buffer.end() - 1);

   return res;
}

1 个答案:

答案 0 :(得分:1)

如果setlocale的第二个参数为NULL,则除了返回当前语言环境外,它不会做任何其他事情。但是你没有那样做。您正在向其发送一个完全由单个nil字节(也称为"")组成的字符串。我的setlocale手册页说

  

如果语言环境是一个空字符串“”,则根据环境变量设置应修改的语言环境的每个部分。详细信息取决于实现。

所以这对您来说是将语言环境设置为用户指定的内容或系统默认值。

完全不运行setlocale可能会使当前语言环境在系统上处于未初始化状态或为NULL,这就是为什么您的程序在没有该设置的情况下会失败。

另外两页有关您正在使用的内容的手册页

  

mbstowcs()的行为取决于当前语言环境的LC_CTYPE类别。

     

wcstombs()的行为取决于当前语言环境的LC_CTYPE类别。

如果您根本没有设置语言环境,那么这些例程大概就是失败的原因。

我想您可能不需要在每次调用这些例程时都运行setlocale语句,但是您需要确保在运行它们之前至少运行一次。

就根据当前语言环境发生的变化而言,我相信这将是将多字节字符串转换为宽字符的正确方式,反之亦然。我认为由于这些差异,这些例程的手册页含糊不清。就个人而言,我希望它设置一些示例,例如“如果当前语言环境为C,则多字节字符串为ASCII字符”。我猜想至少还有一个将其解释为UTF-8,但我对不同的语言环境了解不足,无法确切地说出是哪个语言环境。可能至少在一个语言环境中,多字节字符串碰巧是每个字符编码另外两个字节,但是C和C ++仍会将其视为字节。

编辑:考虑到这一点,考虑到您添加到示例代码中的字符,可以明确地声明使用不支持中文字符的语言环境将导致最终的printf报告长度为-1,包括默认的C语言环境。在这种情况下,缓冲区的内容没有由标准明确指定-至少,我的阅读表明,缓冲区的值可能是所有字符,直到但不包括未能转换的字符。尽管C ++文档和C文档都没有说明关于无法转换的字符会发生什么情况。我没有为官方标准付费,但是我有最新免费版本的副本。 C ++ 17遵循C17。 C17也拒绝评论此功能的这一方面。对于wcsrtombs,它明确指出转换状态未指定。但是,在wcstombs_s上,C17状态为

  

如果在不转换宽宽度字符的情况下停止转换并且dst不是空指针,则将空字符存储在dst指向的数组中,紧跟已存储的任何多字节字符之后。

在我自己上面的OP提供的代码的实验中,看来Fedora 28上的wcstombs实现只是避免对缓冲区进行任何进一步的更改。这似乎向我表明,如果代码的确切行为对于这种情况很重要,则可以改用wcstombs_s。但是至少,您只需要检查返回的长度是否为-1,如果是,则报告错误,而不是假定转换有效。