【问题标题】:How to fix locale?如何修复语言环境?
【发布时间】:2017-03-10 23:24:53
【问题描述】:

添加 ru_RU.CP1251 语言环境(在 debian 上取消注释 ru_RU.CP1251 在 /etc/locale.gen 并运行 sudo locale-gen)和 使用gcc -fexec-charset=cp1251 test.c 编译以下程序(输入文件为UTF-8 格式)。结果是空的。只是字母“я”是错误的。 其他字母确定是小写还是大写都可以。

#include <locale.h>
#include <ctype.h>
#include <stdio.h>
int main (void)
{
  setlocale(LC_ALL, "ru_RU.CP1251");
  char c = 'я';
  int i;
  char z;
  for (i = 7; i >= 0; i--) {
    z = 1 << i;
    if ((z & c) == z) printf("1"); else printf("0");
  }
  printf("\n");

  if (islower(c))
    printf("lowercase\n");
  if (isupper(c))
    printf("uppercase\n");
  return 0;
}

为什么islower() 和isupper() 都不能处理я 的字母?

【问题讨论】:

  • char 是否足够大以存储 я? islower() 的原型表明int 会是更好的选择。
  • islower 和 isupper 不能按预期使用多字节字符,请查看 iswupper 和 iswlower
  • @KeineLust 我使用 cp1251,它是 8 位编码。我不需要宽字符。试试字母 'ю' - 它工作得很好。只有字母“я”不起作用。我需要解决这个问题。
  • @IgorLiferenko,你试过ru_RU.UTF-8 吗?如果输入是 utf-8,如果您不首先在代码集之间转换字符代码,那么尝试将其显示为 cp-1251 是没有意义的。
  • @mouviciel:常规 isxxxxx() 函数的原型具有 int 作为参数类型,因为您可以传递任何有效的“字符编码为 unsigned char”值或 EOF,这意味着一个的char 类型不能用作形式参数类型(因为它不能接受足够广泛的值)。

标签: c locale


【解决方案1】:

Jonathan Leffler 对 OP 的第一条评论是正确的。需要isxxx()(和iswxxx())函数来处理EOF(WEOF)参数 (可能是万无一失的)。 这就是选择int 作为参数类型的原因。当我们传递 char 类型的参数或字符文字时,它是 提升为int(保留标志)。而且因为默认情况下char 类型和字符文字在 gcc 中签名, 0xFF 变成了-1,这不巧是EOF 的值。

因此总是在将 char 类型的参数(以及代码为 0xFF 的字符文字)传递给函数时,使用 int 参数类型进行显式类型转换(不要指望 char 的无符号性,因为它是实现定义的)。类型转换可以通过(unsigned char) 完成,也可以通过(uint8_t) 完成,后者的类型更少(您必须包含stdint.h)。

另请参阅https://sourceware.org/bugzilla/show_bug.cgi?id=20792 和 Why passing char as parameter to islower() does not work correctly?

【讨论】:

    【解决方案2】:

    答案是 CP 1251 中该字符的小写版本的编码是十进制 255,而您的实现的 islower() 和 isupper() 不接受或返回该值(通常被解释为 EOF) .

    您需要跟踪运行时库的源代码,以了解它的作用和原因。

    解决方案是编写您自己的实现,或者包装您拥有的实现。就个人而言,我从不直接使用这些函数,因为有很多陷阱。

    【讨论】:

    • 为什么putchar()、fputc()和putc()的参数类型不是char,而putwchar()、fputwc()和putwc()的参数类型是wchar_t?另外,为什么在示例中 man mbstowcs 类型的变量 wchar_t 被传递给 iswlower() ?这与iswlower() 采用wint_t 的事实相矛盾。例子错了吗?顺便说一句,你用什么包装器?你看到这个问题了吗? stackoverflow.com/questions/40601645/…
    • @IgorLiferenko:我总是编写自己的包装器。对于其他人,您应该提出一个新问题。
    • 我在这里问了这个问题:stackoverflow.com/questions/40626189/…
    【解决方案3】:

    Igor,如果您的文件是 UTF-8,那么尝试使用代码页 1251 是没有意义的,因为它与 utf-8 编码没有任何共同之处。只需使用语言环境ru_RU.UTF-8,您就可以毫无问题地显示您的文件。或者,如果您坚持使用ru_RU.CP1251,则需要先将文件从utf-8 编码转换为cp1251(您可以使用iconv(1) 实用程序)

    iconv --from-code=utf-8 --to-code=cp1251 your_file.txt > your_converted_file.txt
    

    另一方面,--fexec-charset=cp1251 仅影响可执行文件中使用的字符,但您尚未在源代码中指定要在字符串文字中使用的输入字符集。可能,编译器是根据环境(您在 LANG 或 LC_CHARSET 环境变量中设置的)来确定的

    只有在您准确控制每个阶段使用的语言环境后,您才能获得一致的结果。

    努力将所有国家/地区切换为通用字符集 (UTF) 的主要原因正是为了不必在每个阶段处理所有这些语言环境设置。

    如果您总是处理以 CP1251 编码的文档,则您需要对计算机上的所有内容使用该编码,但是当您收到一些以 utf-8 编码的文档时,您必须将其转换为能够正确看待它。

    我主要建议您切换到 utf-8,因为它是一种支持所有国家/地区字符集的编码,但目前,这个决定只属于您。

    注意

    在 debian linux 上:

    $ sed 's/^/    /' pru-$$.c 
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    #include <locale.h>
    
    #define P(f,v) printf(#f"(%d /* '%c' */) => %d\n", (v), (v), f(v))
    #define Q(v) do{P(isupper,(v));P(islower,(v));}while(0)
    
    int main()
    {
        setlocale(LC_ALL, "");
        Q(0xff);
    }
    

    编译

    $ make pru-$$
    cc    pru-1342.c   -o pru-1342
    

    使用ru_RU.CP1251 语言环境执行

    $ locale | sed 's/^/    /'
    LANG=ru_RU.CP1251
    LANGUAGE=
    LC_CTYPE="ru_RU.CP1251"
    LC_NUMERIC="ru_RU.CP1251"
    LC_TIME="ru_RU.CP1251"
    LC_COLLATE="ru_RU.CP1251"
    LC_MONETARY="ru_RU.CP1251"
    LC_MESSAGES="ru_RU.CP1251"
    LC_PAPER="ru_RU.CP1251"
    LC_NAME="ru_RU.CP1251"
    LC_ADDRESS="ru_RU.CP1251"
    LC_TELEPHONE="ru_RU.CP1251"
    LC_MEASUREMENT="ru_RU.CP1251"
    LC_IDENTIFICATION="ru_RU.CP1251"
    LC_ALL=
    
    $ pru-$$
    isupper(255 /* 'я' */) => 0
    islower(255 /* 'я' */) => 512
    

    所以,glibc 没有问题,问题出在你的代码中。

    【讨论】:

    • 嗯,这取决于您的软件发行版,因为如果您的系统管理员尚未配置,您应该更改您的系统配置以允许您想要的语言环境。如果包含它,您可以通过调整环境来更改语言环境(变量LC_* 和LANG,只需深入了解locale(1) 工具)语言环境是每个用户(或每个会话)配置问题。您首先需要将它们包含在您的语言环境库中,然后您必须使用环境变量进行调整。编译语言环境信息是一个依赖于系统的问题,因此您需要深入了解您的系统。
    • utf-8 是 8 位语言环境,信不信由你,您使用 8 位字符来处理。顺便说一句,您没有在问题中指定修复语言环境是您想要的。区域设置由几个不兼容的软件包管理,具体取决于您拥有的软件发行版(在 macintosh 中,您有从 BSD 派生的软件包,在 linux 中您通常有 gettext 软件包,两者都有不同的工具和配置信息)指定你想要什么确切地说,我将能够指定更多。
    • @IgorLiferenko,是的,因为向编译器添加选项只会影响您的代码,而不会影响执行实际输入/输出的库中的代码。但我不会再讨论这个了。
    • 无论哪种情况,添加 -fexec-charset=cp1251 不会使您的代码接受 utf-8 作为本机...。你不明白utf-8和cp1251没有关系,你的文件就是那个编码的……你得先把你的文件转换一下,不然你就没法用它做任何有用的事情了。跨度>
    • 发布错误报告并不意味着您的主张是正确的。你知道你的 any 语言环境中的 0xff 是什么意思吗?要求任何大写字母有意义吗?无论如何,我认为你完全错了。去议会提出要求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-10
    • 2019-03-20
    • 1970-01-01
    • 1970-01-01
    • 2014-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多