使用strtod strtof atof printf进行区域设置不变的字符串处理?

时间:2017-01-22 18:26:13

标签: c++ c locale standard-library

是否有计划添加在当前区域设置下不变的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 = '.';
}

这些函数预处理其输入,因此可以在假设

的情况下独立于当前语言环境使用
  1. 分隔符是一个字节
  2. 在这些修复功能和对任何受影响的功能的调用之间没有调用setlocale
  3. 转换前可以修改字符串
  4. (1)意味着预处理方法在异域语言环境中中断(例如,请参阅https://en.wikipedia.org/wiki/Decimal_mark#Hindu.E2.80.93Arabic_numeral_system)。 (2)暗示如果没有锁定,预处理方法就不能是线程安全的,并且必须将锁定添加到C库中。 (3)愚蠢。

    如果只能将对字符串处理函数的单个调用的语言环境指定为参数,而不影响任何其他线程,则不会应用这些限制。

    问题:

    1. 是否有任何针对此缺陷的WG14或WG21的报告?
    2. 如果是这样,为什么这些并没有合并到标准中?它只不过是一个以语言环境为参数的新函数集。
    3. 规范的解决方法是什么?
    4. 更新

      通过互联网搜索后,我发现了* _l函数,可在FreeBSD,GNU / Linux和MacOSX上找到。 Windows上也存在类似的功能。这些解决了我的问题,但是这些不在POSIX中,它是C的超集(不是真的,POSIX放松了指针)。所以问题1和问题2仍然存在。

3 个答案:

答案 0 :(得分: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手册页说明要使用的标头是<xlocale.h>而不是<locale.h>;这也许可以通过标准来解决。除非BSD功能的设计存在重大缺陷,否则这些缺陷应该是任何标准化工作的良好基础,无论是在POSIX还是标准C下。

BSD设计的一个问题可能是locale_t结构是通过值传递的,而不是通过(常量限制的)指针传递的,这有点令人惊讶。但是,它与POSIX函数一致,例如:

int   isalpha_l(int, locale_t);

也可以设计类似的方案来处理时区设置。由于还没有时区类型(而locale_t已经是POSIX的一部分 - 并且可能无需更改为标准C即可采用),因此在设置时需要做更多工作。但是,结合语言环境设置,它可以使单个可执行文件中的时间例程更容易在不同的环境中使用。

答案 1 :(得分:0)

sqlite具有locale independant printf实现,这对您很有帮助,因为它使double与sql语法规则兼容。如果您可以将sqlite包含为依赖项,那么这可能是一个可行的选择。

答案 2 :(得分:0)

Glibc没有特定于语言环境的功能,但是具有POSIX标准的uselocale函数,该功能允许为每个线程设置语言环境。因此,代替提供许多特定于语言环境的功能,可以通过临时更改语言环境来使用任何标准函数(包括包装在库调用中的标准函数):

locale_t original = uselocale(loc);
// use printf/scanf/etc which now use `loc`
uselocale(original);