Linux:打印指定长度的零填充字符串

Linux: printing a zero-padded string of specified length

在 Linux,我正在使用 GNU gcc 4.9.2 版本并在尝试打印指定长度的零填充字符串时出现奇怪的意外行为。这是我正在尝试的代码片段:

#include <cstdio>
#include <cstring>
int main()
{
  char buff[5];
  sprintf(buff,"%04s","12");
  printf("%s\n", buff);
  return 0;
}

虽然 http://www.cplusplus.com/reference/cstdio/printf/ 中给出的文档清楚地指出,在指定填充时,标志用零 (0) 而不是 spaces 向左填充数字。 但是,它正在打印 space 填充的“12”,即“12”而不是“0012”。 补救措施?

manpage of printf 你会找到一个准确的描述:

0

The value should be zero padded. For d, i, o, u, x, X, a, A, e, E, f, F, g, and G conversions, the converted value is padded on the left with zeros rather than blanks. [...] For other conversions, the behavior is undefined.

因此它不是为字符串定义的。一个实现可以用 0 填充,但不是必须的。这是未定义的行为。

以下解决方案使用 %.*s 语法作为格式说明符,因此无需使用 for 循环或多次 printf 调用。

char buff[5];
char str[] = "12";
size_t len = strlen(str);
sprintf(buff, "%.*s%s", len >= 4 ? 0 : (int)(4 - len), "0000", str);

此外,您应该考虑使用 snprintf 来防止缓冲区溢出。

snprintf(buff, sizeof(buff), "%.*s%s", len >= 4 ? 0 : (int)(4 - len), "0000", str);

snprintf 的格式化输出可能因不同的编译器而有所不同。在 Linux gcc 下,它总是附加一个尾随的空字节 '[=30=]'。 Visual Studio 2010 编译器无法确保要写入的字符串是否大于或等于缓冲区。

C11 7.21.6.1p6 明确指出 0s 转换说明符结合使用的行为未定义:

0

For d, i, o, u, x, X, a, A, e, E, f, F, g and G conversions, leading zeros (following any indication of sign or base) are used to pad to the field width rather than performing space padding, except when converting an infinity or NaN. If the 0 and - flags both appear, the 0 flag is ignored. For d, i, o, u, x, X conversions, if a precision is specified, the 0 flag is ignored. For other conversions, the behavior is undefined.

因此 %04s 的行为是 undefined 并且你 lucky 当它不符合你的期望时离开!


因此,这里是 a complete library 最有效地进行左键操作的方法:

char *leftpad(char *str, size_t length, char fill, char buf[]) {
    size_t s_len = strlen(str);
    if (s_len > length) {
        return NULL;
    }

    size_t padding = length - s_len;
    memset(buf, fill, padding);
    strcpy(buf + padding, str);
    return buf;
}

这也适用于 任何 填充字符和任何填充长度。使用示例:

int main(void) {
    char buf[65], *s;
    if (s = leftpad(buf, 64, '0', "12")) {
        puts(s);
    }
}

如果字符串恰好是 负数 十进制字符串(例如,填充将在 - 符号之前),这自然不起作用。