格式化带有多个百分号的字符串

Format string with multiple percent signs

我知道 %% 用于转义字符串中的实际 % 符号,因此 %%%ds 将在以下格式字符串中以 %10s 结束,但我不知道为什么我需要在这个字符串中使用 %%5s

毕竟,只有两个附加参数(BUFFSIZE / 10)。

#define BUFFSIZE 100
char buf[100]={0}
sprintf(buf, "%%5s %%%ds %%%ds", BUFFSIZE / 10, BUFFSIZE / 10);

在运行上面的代码之后,buf将包含字符串,

%10s %10s 

目的是获取一个格式字符串,以便在另一个需要像 sscanf().

这样的格式字符串的函数中使用它

使用您的代码,您将获得:%5s %10s %10s 写入您的 bufsee online,这意味着它将接受三个具有长度标识符的字符串。

%%5s          --> %5s
%%%ds with 10 --> %10s (read it that way: {%%}{%d}{s})

缓冲区 %5s %10s %10s 现在可以在 sscanf() 调用中使用,如图 here

但是有一个最佳实践可以防止由 sscanf() 引起的缓冲区溢出,Kernighan 和 Pike 在他们的书中也有描述 The Practice of Programming, see here on SO.


您可能无法使用 %*s 的原因可能是,请参阅 here on SO:

For printf, the * allows you to specify minimum field width through an extra parameter, i.e. printf("%*d", 4, 100); specifies a field width of 4.

For scanf, the * indicates that the field is to be read but ignored, so that i.e. scanf("%*d %d", &i) for the input "12 34" will ignore 12 and read 34 into the integer i.

% 本身就是一个有效的转换说明符。如 C11 章节 §7.21.6.1/P2 中所述,规定的语法是 (emphasis mine)

Each conversion specification is introduced by the character %. After the %, the following appear in sequence:

  • Zero or more flags [...]

  • An optional minimum field width.

  • An optional precision [...]

  • An optional length modifier [...]

  • A conversion specifier character that specifies the type of conversion to be applied.

然后,从 P8 开始,对于转换说明符

The conversion specifiers and their meanings are:

......

%

A % character is written. No argument is converted. The complete conversion specification shall be %%.

因此,基于贪婪的方法,编译器会将语法分组为

 ....   %%%ds, BUFFSIZE / 10 ....

作为

 {%%}{%d}{s}
  ^^--------------------------Replaced as %
      ^^----------------------Actual conversion specification happens, argument is used
         ^^------------------just part of final output

最终产生

  %Xs    //where X is the value of (BUFFSIZE / 10)

这是一个有效的格式字符串(%,最小字段宽度,转换说明符,全部按顺序),同样,稍后使用。

OP 正在根据参数大小计算格式字符串。给定参数,字符串将包含 %5s %10s %10s,它可以与 printfscanf:

一起使用
printf("%5s %10s %10s", "A", "B", "C");

输出:

    A          B          C

char a[6], b[11], c[11];
scanf("%5s %10s %10s", a, b, c);

会将 3 个字符串读入 abc,但会限制为每个字符串读取的字符数以防止缓冲区溢出。

但是请注意,对于 printf 的情况,没有必要计算发布的字符串,因为您可以使用:

printf("%5s %*s %*s", "A", BUFFSIZE / 10, "B", BUFFSIZE / 10, "C");

不幸的是,scanf() 将不同的语义附加到 * 格式修饰符,并且无法指定要使用参数存储的最大字符数,只能使用格式字符串中的数字,因此需要一个单独的格式化步骤。