【问题标题】:Cross Platform Support for sprintf's Format '-Flag跨平台支持 sprintf 的 Format '-Flag
【发布时间】:2017-11-15 08:54:28
【问题描述】:

Single UNIX Specification Version 2 指定sprintf 的格式'-标志行为为:

十进制转换结果的整数部分(%i%d%u%f%g%G)将使用千位分组字符进行格式化[ 1]

我在 规范中找不到'-flag 格式。 g++ even warns:

ISO C++11 不支持 ' printf 标志

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

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

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

【问题讨论】:

  • 不清楚你在问什么。 “我在 c 或 c++ 规范中找不到格式 '-flag。”。你已经有了答案。你有什么问题?
  • 哦,伙计,我以为只有 C++ 对所有问题都强制投反对票。我猜C也是?或者对这个经过仔细研究的问题投反对票是否有实际原因?
  • 那个标志不是 gcc,而是你系统上的 libc。
  • @Olaf 如果您同意我的观点,即它不在 C 标准中,并且我没有只是看规范,我正在寻找一种方法来复制 libc 的行为以跨平台的方式。如果我只是在 C 标准中错过了它,并且这是 Microsoft 未能完全实现该标准的一个实例,我可以使用 '-flag 格式。
  • 没有跨平台的方式来使用 C 标准的 POSIX 扩展。它不一定在 POSIX 系统上普遍实现,更不用说在其他地方了。如果您需要该功能,则必须实现它。您需要的信息可在标准 C 中的struct lconv 中找到localeconv()。决定您将使用哪些函数进行格式化,但可移植性要求您使用自定义函数进行格式化,并根据需要实现。

标签: c c++ c printf cross-platform number-formatting sus


【解决方案1】:

标准 C 不直接提供格式化功能,但它确实提供了在特定于语言环境的基础上检索格式应该是什么的……规范的能力。因此,您可以自行检索区域设置的正确格式规范,然后将其用于格式化您的数据(但即便如此,这也不是一件容易的事)。例如,这里有一个格式化long数据的版本:

#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 的东西来进行转换,然后将格式插入到产生的字符串中。

【讨论】:

  • lconv 的另一条注释似乎 negative_sign 是“用于表示负货币数量的字符串”,所以我认为应该将 '-' 用于非货币整数。跨度>
  • @JonathanMee:嗯...非常正确。不知道我是怎么错过的(或者我只是没看)。
  • 所以我将这个答案中的概念移植到 C++ 并链接了这个问题。希望这能得到您的认可。 stackoverflow.com/a/44549637/2642059
  • @JonathanMee:这并不困扰我它在 C++ 中似乎没那么有用。 C++ 长期以来一直要求 iostream 能够识别区域设置,因此流将根据其所在的区域设置标点数字。 std::cout.imbue(std::locale("")); std::cout &lt;&lt; "1234567.89; 将打印(在我的机器上)1,234,567.89,但如果(因为我使用的是匿名语言环境),这将取决于您的环境的配置方式。至少在我的经验中,所有典型的编译器/库都支持这一点很长时间了。
  • 是的,我同意。这个问题实际上是由我在不使用stringstream 的情况下执行此操作的要求产生的。因此需要跳过所有这些障碍。
猜你喜欢
  • 2010-09-07
  • 2022-09-23
  • 2021-09-04
  • 1970-01-01
  • 1970-01-01
  • 2019-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多