对 sprintf 格式 '-Flag 的跨平台支持

2023-12-02

The 单一 UNIX 规范版本 2指定sprintf的格式'- 将行为标记为:

The integer portion of the result of a decimal conversion (%i, %d, %u, %f, %g or %G) will be formatted with thousands' grouping characters[1]

我找不到格式'- 标志在c or the c++规格。g++ 甚至发出警告:

ISO C++11 不支持' printf flag

在 Visual C 中,该标志无法被识别甚至发出警告;printf("%'d", foo) outputs:

'd

我希望能够编写使用该格式行为的符合 C 标准的代码'-旗帜。因此,我正在寻找以下答案之一:

  1. C-标准格式规范'-flag
  2. gcc 格式的跨平台兼容外推'-flag
  3. 证明跨平台外推是不可能的

标准 C 不直接提供格式化功能,但它确实提供了检索特定于语言环境的格式化规范的能力。因此,您需要检索区域设置的正确格式规范,然后将其用于格式化您的数据(但即便如此,这也不是一件简单的事情)。例如,这是一个格式化版本long data:

#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <limits.h>

static int next_group(char const **grouping) {
    if ((*grouping)[1] == CHAR_MAX)
        return 0;
    if ((*grouping)[1] != '\0')
        ++*grouping;
    return **grouping;
}

size_t commafmt(char   *buf,            /* Buffer for formatted string  */
                int     bufsize,        /* Size of buffer               */
                long    N)              /* Number to convert            */
{
    int i;
    int len = 1;
    int posn = 1;
    int sign = 1;
    char *ptr = buf + bufsize - 1;

    struct lconv *fmt_info = localeconv();
    char const *tsep = fmt_info->thousands_sep;
    char const *group = fmt_info->grouping;
    // char const *neg = fmt_info->negative_sign;
    size_t sep_len = strlen(tsep);
    size_t group_len = strlen(group);
    // size_t neg_len = strlen(neg);
    int places = (int)*group;

    if (bufsize < 2)
    {
ABORT:
        *buf = '\0';
        return 0;
    }

    *ptr-- = '\0';
    --bufsize;
    if (N < 0L)
    {
        sign = -1;
        N = -N;
    }

    for ( ; len <= bufsize; ++len, ++posn)
    {
        *ptr-- = (char)((N % 10L) + '0');
        if (0L == (N /= 10L))
            break;
        if (places && (0 == (posn % places)))
        {
            places = next_group(&group);
            for (int i=sep_len; i>0; i--) {
                *ptr-- = tsep[i-1];
                if (++len >= bufsize)
                    goto ABORT;
            }
        }
        if (len >= bufsize)
            goto ABORT;
    }

    if (sign < 0)
    {
        if (len >= bufsize)
            goto ABORT;
        *ptr-- = '-';
        ++len;
    }

    memmove(buf, ++ptr, len + 1);
    return (size_t)len;
}

#ifdef TEST
#include <stdio.h>

#define elements(x) (sizeof(x)/sizeof(x[0]))

void show(long i) {
    char buffer[32];

    commafmt(buffer, sizeof(buffer), i);
    printf("%s\n", buffer);
    commafmt(buffer, sizeof(buffer), -i);
    printf("%s\n", buffer);
}


int main() {

    long inputs[] = {1, 12, 123, 1234, 12345, 123456, 1234567, 12345678 };

    for (int i=0; i<elements(inputs); i++) {
        setlocale(LC_ALL, "");
        show(inputs[i]);
    }
    return 0;
}

#endif

这确实有一个错误(但我认为相当小)。在二进制补码硬件上,它不会正确转换最大负数,因为它尝试使用以下命令将负数转换为其等效的正数N = -N;在二进制补码中,最大负数没有对应的正数,除非将其提升为更大的类型。解决这个问题的一种方法是将数字提升为相应的无符号类型(但这有点不简单)。

对其他整数类型实现相同的操作相当简单。对于浮点类型来说需要更多的工作。正确转换浮点类型(即使没有格式化)对于他们来说已经足够多了,我至少会考虑使用类似的东西sprintf进行转换,然后将格式插入生成的字符串中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对 sprintf 格式 '-Flag 的跨平台支持 的相关文章

随机推荐