如何修复语言环境?

时间:2016-10-28 05:47:43

标签: c locale

添加ru_RU.CP1251语言环境(在ru_RU.CP1251中的debian取消注释/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()都没有在字母я上工作?

3 个答案:

答案 0 :(得分:1)

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没有故障,故障在你的代码中。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

Jonathan Leffler对OP的第一个评论是真的。处理isxxx()iswxxx())参数需要EOF(和WEOF)个函数 (可能是万无一失的)。 这就是选择int作为参数类型的原因。当我们传递类型char或字符文字的参数时,它是 晋升为int(保留标志)。并且因为默认情况下char类型和字符文字是用gcc签名的, 0xFF变为-1,这与EOF的价值不一致。

因此总是在使用0xFF参数类型向函数传递char类型的参数(以及带有代码int的字符文字)时进行显式类型转换(不要)依赖于char的无符号性,因为它是实现定义的)。可以通过(unsigned char)(uint8_t)进行类型转换,类型较少(必须包含stdint.h)。

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