使用 strtod strtof atof printf 进行语言环境不变的字符串处理?
Locale-invariant string processing with strtod strtof atof printf?
是否有计划添加在当前语言环境下不变的 C 标准库字符串处理函数版本?
目前有很多脆弱的解决方法,例如,来自 jansson/strconv.c:
static void to_locale(strbuffer_t *strbuffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(strbuffer->value, '.');
if(pos)
*pos = *point;
}
static void from_locale(char *buffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, *point);
if(pos)
*pos = '.';
}
这些函数对其输入进行预处理,因此在假设
下,它可以独立于当前区域设置使用
- 分隔符是一个字节
- 在这些修复函数和对任何受影响函数的调用之间没有调用
setlocale
- 转换前可以修改字符串
(1) 暗示预处理方法在异国情调的环境中中断(有关示例,请参见 https://en.wikipedia.org/wiki/Decimal_mark#Hindu.E2.80.93Arabic_numeral_system)。
(2) 暗示预处理方法在没有锁的情况下不能是线程安全的,并且必须将锁添加到 C 库中。 (3) 就是傻。
如果只能为字符串处理函数的单个调用指定语言环境作为参数,而不影响任何其他线程,则 none 这些限制将适用。
问题:
- WG14 或 WG21 是否有解决此缺陷的报告?
- 如果是这样,为什么没有将这些合并到标准中?它只不过是一组以语言环境为参数的新函数。
- 规范的解决方法是什么?
更新:
通过互联网搜索后,我找到了 *_l 函数,在 FreeBSD、GNU/Linux 和 MacOSX 上可用。 Windows 上也存在类似的功能。这些解决了我的问题,但是这些不在 POSIX 中,它是 C 的超集(不是真的,POSIX 放宽了指针)。所以问题 1 和 2 仍然悬而未决。
BSD 和 macOS Sierra(以及它之前的 Mac OS X)支持 _l
允许您指定语言环境的函数,而不是依赖于当前语言环境。例如:
int
fprintf_l(FILE * restrict stream, locale_t loc, const char * restrict format, ...);
int
printf_l(locale_t loc, const char * restrict format, ...);
int
snprintf_l(char * restrict str, size_t size, locale_t loc, const char * restrict format, ...);
int
sprintf_l(char * restrict str, locale_t loc, const char * restrict format, ...);
和:
int
fscanf_l(FILE * restrict stream, locale_t loc, const char * restrict format, ...);
int
scanf_l(locale_t loc, const char * restrict format, ...);
int
sscanf_l(const char * restrict str, locale_t loc, const char * restrict format, ...);
作为一般设计,这似乎是明智的。类型 locale_t
不是标准 C 的一部分,而是 POSIX 的一部分(并在 <locale.h>
中定义),并在 <ctype.h>
等其他地方使用。 BSD 手册页说要使用的 header 是 <xlocale.h>
而不是 <locale.h>
;这可能会由标准修复。除非 BSD 函数的设计存在重大缺陷,否则这些应该是任何标准化工作的良好基础,无论是在 POSIX 还是标准 C.
下
BSD 设计的一个问题可能是 locale_t
结构是按值传递的,而不是按(常量限制)指针传递的,这有点令人惊讶。但是与POSIX函数一致,如:
int isalpha_l(int, locale_t);
也可以设计一个类似的方案来处理时区设置。由于还没有时区类型(而 locale_t
已经是 POSIX 的一部分,并且可能无需更改为标准 C 就可以采用).但是,结合语言环境设置,它可以使时间例程更容易从单个可执行文件在不同的环境中使用。
sqlite 有 locale independant printf 实现,这对你的事情很有用,因为它使双打与 sql 语法规则兼容。如果您可以包含 sqlite 作为依赖项,那么这可能是一个可行的选择。
Glibc 没有特定于语言环境的函数,但它具有 POSIX-标准 uselocale
函数,允许为每个线程设置语言环境。因此,通过临时更改语言环境,可以使用任何标准函数,而不是提供许多特定于语言环境的函数——包括包装在库调用中的函数:
locale_t original = uselocale(loc);
// use printf/scanf/etc which now use `loc`
uselocale(original);
是否有计划添加在当前语言环境下不变的 C 标准库字符串处理函数版本?
目前有很多脆弱的解决方法,例如,来自 jansson/strconv.c:
static void to_locale(strbuffer_t *strbuffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(strbuffer->value, '.');
if(pos)
*pos = *point;
}
static void from_locale(char *buffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, *point);
if(pos)
*pos = '.';
}
这些函数对其输入进行预处理,因此在假设
下,它可以独立于当前区域设置使用- 分隔符是一个字节
- 在这些修复函数和对任何受影响函数的调用之间没有调用
setlocale
- 转换前可以修改字符串
(1) 暗示预处理方法在异国情调的环境中中断(有关示例,请参见 https://en.wikipedia.org/wiki/Decimal_mark#Hindu.E2.80.93Arabic_numeral_system)。 (2) 暗示预处理方法在没有锁的情况下不能是线程安全的,并且必须将锁添加到 C 库中。 (3) 就是傻。
如果只能为字符串处理函数的单个调用指定语言环境作为参数,而不影响任何其他线程,则 none 这些限制将适用。
问题:
- WG14 或 WG21 是否有解决此缺陷的报告?
- 如果是这样,为什么没有将这些合并到标准中?它只不过是一组以语言环境为参数的新函数。
- 规范的解决方法是什么?
更新:
通过互联网搜索后,我找到了 *_l 函数,在 FreeBSD、GNU/Linux 和 MacOSX 上可用。 Windows 上也存在类似的功能。这些解决了我的问题,但是这些不在 POSIX 中,它是 C 的超集(不是真的,POSIX 放宽了指针)。所以问题 1 和 2 仍然悬而未决。
BSD 和 macOS Sierra(以及它之前的 Mac OS X)支持 _l
允许您指定语言环境的函数,而不是依赖于当前语言环境。例如:
int fprintf_l(FILE * restrict stream, locale_t loc, const char * restrict format, ...); int printf_l(locale_t loc, const char * restrict format, ...); int snprintf_l(char * restrict str, size_t size, locale_t loc, const char * restrict format, ...); int sprintf_l(char * restrict str, locale_t loc, const char * restrict format, ...);
和:
int fscanf_l(FILE * restrict stream, locale_t loc, const char * restrict format, ...); int scanf_l(locale_t loc, const char * restrict format, ...); int sscanf_l(const char * restrict str, locale_t loc, const char * restrict format, ...);
作为一般设计,这似乎是明智的。类型 locale_t
不是标准 C 的一部分,而是 POSIX 的一部分(并在 <locale.h>
中定义),并在 <ctype.h>
等其他地方使用。 BSD 手册页说要使用的 header 是 <xlocale.h>
而不是 <locale.h>
;这可能会由标准修复。除非 BSD 函数的设计存在重大缺陷,否则这些应该是任何标准化工作的良好基础,无论是在 POSIX 还是标准 C.
BSD 设计的一个问题可能是 locale_t
结构是按值传递的,而不是按(常量限制)指针传递的,这有点令人惊讶。但是与POSIX函数一致,如:
int isalpha_l(int, locale_t);
也可以设计一个类似的方案来处理时区设置。由于还没有时区类型(而 locale_t
已经是 POSIX 的一部分,并且可能无需更改为标准 C 就可以采用).但是,结合语言环境设置,它可以使时间例程更容易从单个可执行文件在不同的环境中使用。
sqlite 有 locale independant printf 实现,这对你的事情很有用,因为它使双打与 sql 语法规则兼容。如果您可以包含 sqlite 作为依赖项,那么这可能是一个可行的选择。
Glibc 没有特定于语言环境的函数,但它具有 POSIX-标准 uselocale
函数,允许为每个线程设置语言环境。因此,通过临时更改语言环境,可以使用任何标准函数,而不是提供许多特定于语言环境的函数——包括包装在库调用中的函数:
locale_t original = uselocale(loc);
// use printf/scanf/etc which now use `loc`
uselocale(original);