如何修复语言环境?
How to fix locale?
添加 ru_RU.CP1251 语言环境(在 debian 上取消注释 /etc/locale.gen
和 运行 sudo locale-gen
中的 ru_RU.CP1251
)和
使用 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()
都不能处理字母 я
?
Igor,如果您的文件是 UTF-8,那么尝试使用代码页 1251 是没有意义的,因为它与 utf-8 编码没有任何共同之处。只需使用 locale 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 没有问题,问题出在你的代码上。
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 and
答案是 CP 1251 中该字符的小写版本的编码是十进制 255,您的实现的 islower()
和 isupper()
不接受或 return该值(通常被解释为 EOF)。
您需要追踪运行时库的源代码以了解它的作用和原因。
解决方案是编写您自己的实现,或者包装您拥有的实现。就个人而言,我从不直接使用这些功能,因为有很多问题。
添加 ru_RU.CP1251 语言环境(在 debian 上取消注释 /etc/locale.gen
和 运行 sudo locale-gen
中的 ru_RU.CP1251
)和
使用 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()
都不能处理字母 я
?
Igor,如果您的文件是 UTF-8,那么尝试使用代码页 1251 是没有意义的,因为它与 utf-8 编码没有任何共同之处。只需使用 locale 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 没有问题,问题出在你的代码上。
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 and
答案是 CP 1251 中该字符的小写版本的编码是十进制 255,您的实现的 islower()
和 isupper()
不接受或 return该值(通常被解释为 EOF)。
您需要追踪运行时库的源代码以了解它的作用和原因。
解决方案是编写您自己的实现,或者包装您拥有的实现。就个人而言,我从不直接使用这些功能,因为有很多问题。