printf string, variable length item - c

#define SIZE 9
int number=5;
char letters[SIZE]; /* this wont be null-terminated */
...
char fmt_string[20];
sprintf(fmt_string, "%%d %%%ds", SIZE);
/* fmt_string = "%d %9d"... or it should be */
printf(fmt_string, number, letters);
Is there a better way to do this?

There is no need to construct a special format string. printf allows you to specify the precision using a parameter (that precedes the value) if you use a .* as the precision in the format tag.
For example:
printf ("%d %.*s", number, SIZE, letters);
Note: there is a distinction between width (which is a minimum field width) and precision (which gives the maximum number of characters to be printed).
%*s specifies the width, %.s specifies the precision. (and you can also use %*.* but then you need two parameters, one for the width one for the precision)
See also the printf man page (man 3 printf under Linux) and especially the sections on field width and precision:
Instead of a decimal digit string one may write "*" or "*m$" (for some
decimal integer m) to specify that the precision is given in the next
argument, or in the m-th argument, respectively, which must be of type int.

A somewhat unknown function is asprintf. The first parameter is a **char. This function will malloc space for the string so you don't have to do the bookkeeping. Remember to free the string when done.
char *fmt_string;
asprintf(&fmt_string, "%%d %%%ds", SIZE);
printf(fmt_string, number, letters);
free(fmt_string);
is an example of use.

Related

Format string with multiple percent signs

I know %% is used to escape actual % signs in a string, so %%%ds will end up with %10s in the following format string, but I don't know why I need %%5s in this string?
After all, there are only two additional arguments (BUFFSIZE / 10).
#define BUFFSIZE 100
char buf[100]={0}
sprintf(buf, "%%5s %%%ds %%%ds", BUFFSIZE / 10, BUFFSIZE / 10);
After running the code above, the buf will contain the string,
%10s %10s
The purpose is to get a format string to use it in another function that needs a format string like sscanf().
With your code you get: %5s %10s %10s written to your buf, see online, which means it will accept three strings with a length identifier.
%%5s --> %5s
%%%ds with 10 --> %10s (read it that way: {%%}{%d}{s})
That buffer %5s %10s %10s could now be used in a sscanf() call like shown here.
But there is a best practice to prevent a buffer overflow caused by sscanf() which is also described by Kernighan and Pike in their book The Practice of Programming, see here on SO.
The reason why you maybe can't use %*s may be, see 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.
% in itself is a valid conversion specifier. The prescribed syntax is, as mentioned in C11, chapter §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.
Then, from P8, for conversion specifiers
The conversion specifiers and their meanings are:
......
%
A % character is written. No argument is converted. The complete
conversion specification shall be %%.
So, based on greedy approach, compiler will group a syntax like
.... %%%ds, BUFFSIZE / 10 ....
as
{%%}{%d}{s}
^^--------------------------Replaced as %
^^----------------------Actual conversion specification happens, argument is used
^^------------------just part of final output
which finally produces
%Xs //where X is the value of (BUFFSIZE / 10)
which is a valid format string (%, minimum field width, conversion specifier, all in order), again, to be used later.
The OP is computing a format string from parametric sizes. Given the arguments, the string will contain %5s %10s %10s, which can be used with either printf or scanf:
printf("%5s %10s %10s", "A", "B", "C");
outputs:
A B C
char a[6], b[11], c[11];
scanf("%5s %10s %10s", a, b, c);
will read 3 strings into a, b, c but limits the number of characters read for each string to prevent buffer overflow.
Note however that for the printf case, it is not necessary to compute the string as posted, since you could have used:
printf("%5s %*s %*s", "A", BUFFSIZE / 10, "B", BUFFSIZE / 10, "C");
Unfortunately, scanf() attaches different semantics to the * format modifier and it is not possible to specify the maximum number of characters to store with an argument, only with digits in the format string, hence need for a separate formatting step.

Specify variable number of decimal places to printf

I'm wondering how I can use scanf() to allow the user type in the number of decimal places he wants the answer given to, and how to input this variable into the printf format specifier. For example
printf("The answer is %.(variable wanted here)f", answer);
If you use * for your field precision specifier, it tells printf that it is variable. You then specify an additional preceding int argument to tell printf the desired precision.
From printf(3):
Instead of a decimal digit string one may write "*" or "*m$" (for some decimal integer m) to specify that the
field width is given in the next argument, or in the m-th argument, respectively, which must be of type int.
Note that this also works to set the maximum number of characters to print from a string.
#include <stdio.h>
int main(void)
{
int places = 3;
printf("%0.*f\n", places, 1.23456789);
printf("%0.*f\n", places, 6.7);
char buf[] = "Stack OverflowXXXXXXXX";
printf("%.*s\n", 14, buf);
return 0;
}
Output:
1.235
6.700
Stack Overflow

How to use a scanf width specifier of 0?

How to use a scanf width specifier of 0?
1) unrestricted width (as seen with cywin gcc version 4.5.3)
2) UB
3) something else?
My application (not shown) dynamically forms the width specifier as part of a larger format string for scanf(). Rarely it would create a "%0s" in the middle of the format string. In this context, the destination string for that %0s has just 1 byte of room for scanf() to store a \0 which with behavior #1 above causes problems.
Note: The following test cases use constant formats.
#include <memory.h>
#include <stdio.h>
void scanf_test(const char *Src, const char *Format) {
char Dest[10];
int NumFields;
memset(Dest, '\0', sizeof(Dest)-1);
NumFields = sscanf(Src, Format, Dest);
printf("scanf:%d Src:'%s' Format:'%s' Dest:'%s'\n", NumFields, Src, Format, Dest);
}
int main(int argc, char *argv[]) {
scanf_test("1234" , "%s");
scanf_test("1234" , "%2s");
scanf_test("1234" , "%1s");
scanf_test("1234" , "%0s");
return 0;
}
Output:
scanf:1 Src:'1234' Format:'%s' Dest:'1234'
scanf:1 Src:'1234' Format:'%2s' Dest:'12'
scanf:1 Src:'1234' Format:'%1s' Dest:'1'
scanf:1 Src:'1234' Format:'%0s' Dest:'1234'
My question is about the last line. It seems that a 0 width results in no width limitation rather than a width of 0. If this is correct behavior or UB, I'll have to approach the zero width situation another way or are there other scanf() formats to consider?
The maximum field width specifier must be non-zero. C99, 7.19.6.2:
The format shall be a multibyte character sequence, beginning and ending in its initial
shift state. The format is composed of zero or more directives: one or more white-space
characters, an ordinary multibyte character (neither % nor a white-space character), or a
conversion specification. Each conversion specification is introduced by the character %.
After the %, the following appear in sequence:
— An optional assignment-suppressing character *.
— An optional nonzero decimal integer that specifies the maximum field width (in
characters).
— An optional length modifier that specifies the size of the receiving object.
— A conversion specifier character that specifies the type of conversion to be applied.
So, if you use 0, the behavior is undefined.
This came from 7.21.6.2 of n1570.pdf (C11 standard draft):
After the %, the following appear in sequence:
— An optional assignment-suppressing character *.
— An optional decimal integer greater than zero that specifies the
maximum field width (in characters).
...
It's undefined behaviour, because the C standard states that your maximum field width must be greater than zero.
An input item is defined as the longest sequence of input characters
which does not exceed any specified field width and ...
What is it you wish to achieve by reading a field of width 0 and assigning it as a string (empty string) into Dest? Which actual problem are you trying to solve? It seems more clear to just assign like *Dest = '\0';.

Does %*d mean one or more integer?

Does %*d mean one or more integer? in sprintf function
It is used to manipulate the minimum width of the numerical value printed, and it specifically means that the width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted. In other words, if you wanted to use a width that was stored in a variable, you could do this:
int width = /* something */;
printf("%*d", width, value);
EDIT: Oops! Correct syntax for sprintf is:
sprintf(buffer, "%*d", width, value);
In the case of sprintf, it means you'll pass two integers, one specifies the field width, and the other the value you're going to convert.
IOW:
sprintf(buffer, "%5d", value);
is essentially the same as:
sprintf(buffer, "%*d", 5, value);
Much like when you're passing values as widths as literals in the format string, you can specify both a width and a precision if you want, something like this:
sprintf(buffer, "%*.*d", 5, 2, value);
It's also worth noting that with scanf and company, a "*" in the format string has a completely different meaning (to convert some input, but not assign the result to anything).
It is similar to %2d, which is "print a 2 length integer", except the length is specified as a parameter (not sure the order). Reference
The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted.
The * refers to a the width or spacing of the integer in this case. The * is a place holder that lets you specify the width as an argument preceding the argument to be formatted (the integer in this case)
printf("The integer will be printed 10 spaces to the right: %*d", 10, 50);

What does the %*s format specifier mean?

In some code that I have to maintain, I have seen a format specifier %*s . Can anybody tell me what this is and why it is used?
An example of its usage is like:
fprintf(outFile, "\n%*s", indent, "");
It's used to specify, in a dynamic way, what the width of the field is:
The width is not specified in the format string, but as an additional
integer value argument preceding the
argument that has to be formatted.
so "indent" specifies how much space to allocate for the string that follows it in the parameter list.
So,
printf("%*s", 5, "");
is the same as
printf("%5s", "");
It's a nice way to put some spaces in your file, avoiding a loop.
Don't use "%*s" on a buffer which is not NULL terminated (packed) thinking that it will print only "length" field.
The format specifier %4s outputs a String in a field width of 4—that is, printf displays the value with at least 4 character positions.
If the value to be output is less than 4 character positions wide, the value is right justified in the field by default.
If the value is greater than 4 character positions wide, the field width expands to accommodate the appropriate number of characters.
To left justify the value, use a negative integer to specify the field width.
References: Java™ How To Program (Early Objects), Tenth Edition
When used in printf and fprintf:
printf("%*s", 4, myValue); is equivalent to printf("%4s", myValue);
It displays the variable with minimum width, rest right-justified spaces. To left-justify the value, use a negative integer.
When used in scanf and sscanf:
/* sscanf example */
#include <stdio.h>
int main ()
{
char sentence []="Rudolph is 12 years old";
char str [20];
int i;
sscanf (sentence,"%s %*s %d",str,&i);
printf ("%s -> %d\n",str,i);
return 0;
}
Output:
Rudolph -> 12
It is used to ignore a string.
* Causes fprintf to pad the output until it is n characters wide, where n is an integer value stored in the a function argument just preceding that represented by the modified type.
printf("%*d", 5, 10) //will result in "10" being printed with a width of 5.
http://www.cplusplus.com/reference/clibrary/cstdio/printf/
The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted.
e.g: printf("%*s", 4, myValue); is equivelant to printf("%4s", myValue);.

Resources