为啥 wctype.h 中的函数在没有 setlocale() 的情况下不起作用?
Posted
技术标签:
【中文标题】为啥 wctype.h 中的函数在没有 setlocale() 的情况下不起作用?【英文标题】:Why functions from wctype.h do not work without setlocale()?为什么 wctype.h 中的函数在没有 setlocale() 的情况下不起作用? 【发布时间】:2017-03-22 20:59:47 【问题描述】:我的设置:glibc 2.24、gcc 6.2.0、UTF-8 环境。
考虑以下示例:
#include <wchar.h>
#include <wctype.h>
#include <locale.h>
int main(void)
setlocale(LC_CTYPE, "en_US.UTF-8");
wchar_t wc = L'я'; /* 00000100 01001111 */
if (iswlower(wc)) return 0;
return 1;
编译并运行它:
$ gcc test.c
$ ./a.out; echo $?
0
现在删除setlocale()
并再次运行。结果不一样:
$ gcc test.c
$ ./a.out; echo $?
1
从技术上讲,这里不需要setlocale()
,因为wctype.h
中的函数使用具有固定编码的宽字符。 (不用说,如果我们希望 ctype.h
中的函数能够正确处理非 ASCII 字符,并且如果我们使用 wchar.h 中的字符转换函数 - 设置外部编码,则需要 setlocale()
。)
为什么没有setlocale()
,这个例子就不能工作?
【问题讨论】:
它怎么知道使用哪个字母? @IgnacioVazquez-Abrams ISO10646 - 对于宽字符是固定的。 gnu.org/software/libc/manual/html_node/Extended-Char-Intro.html ISO 10646 没有命名字母表。 @IgnacioVazquez-Abrams 你有没有注意到我使用了en_US.UTF-8
,但是符号я
不是来自en_US
,它被正确分类了。所以,不,ISO10646 确实命名了字母表。
它不是 en_US 字母表的一部分,但它确实具有该语言的定义排序规则。
【参考方案1】:
C 标准说:
7.25 宽字符分类和映射实用程序
<wctype.h>
...
这些函数的行为受当前语言环境的
LC_CTYPE
类别影响。
此外(5.2.1 字符集)
应定义两组字符及其相关的整理顺序: 写入了哪些源文件(源字符集),以及在 执行环境(执行字符集)。每组又分为一个 基本字符集,其内容由本子条款给出,以及一组零个或多个 特定于语言环境的成员(不是基本字符集的成员)称为 扩展字符。
然后(7.19 常用定义<stddef.h>
)
的所有成员的不同代码
wchar_t
这是一个整数类型,其值范围可以表示在支持的语言环境中指定的最大扩展字符集
因此可能有许多扩展字符集,每个语言环境一个。因此,wchar_t 编码可能依赖于语言环境,因为编码是一组整数代码和一组字符之间的映射,而后者可能是依赖于语言环境的。
鉴于上述情况,<wctype.h>
必须依赖于区域设置。否则,标准将不得不强制要求有一个独立于语言环境的扩展字符集。
在这个特定的例子中,宽字符常量L'я'
(一些整数代码)的值可能对应于C语言环境下扩展字符集的任何成员,也可能不对应。
至于 gcc 和 glibc 的特定行为,为了简单起见,它们总是使用 Unicode/ISO10646/UCS4 作为扩展字符集,在任何语言环境下。但是,它们不会在 C 语言环境下对扩展字符进行分类,因为标准允许它们不必这样做。 (接下来是一个疯狂的猜测)完整的 Unicode 分类表很大,只需要 ASCII 的程序不必为它们的使用付费。
【讨论】:
我试图了解标准背后的原因。如果未指定,则区域设置为"C"
。因此,如果setlocale()
从 OP 中的示例中删除,它无论如何都必须工作,但它没有。是什么阻止 "C"
从 i18n
继承,而 "en_US"
从 i18n
继承? (见 IgnacioVazquez-Abrams 对 OP 的评论)以上是关于为啥 wctype.h 中的函数在没有 setlocale() 的情况下不起作用?的主要内容,如果未能解决你的问题,请参考以下文章
error: No curses/termcap library found
为啥我的自定义函数调用方法中的 println 语句没有出现在日志中?
为啥在 TypeScript 中的“任何”字段类型变量上使用“字符串”函数时没有编译时错误?