How to use snprintf like strncat? - c

char src1[4+1];
char src2[4+1];
char target[4+1];
strncpy(src1, "1234", sizeof(src1));
strncpy(src2, "ABCD", sizeof(src2));
snprintf(target, sizeof(target), "%3s%1s", src1, src2);
target[4] = '\0';
printf("result: %s\n", target);
Result is "1234" but I wanted to make "123A".
strncpy(src1, "1234", sizeof(src1));
strncpy(src2, "ABCD", sizeof(src2));
strncpy(target, src1, 3);
strncat(target+3, src2, 1);
printf("result: %s\n", target);
When I use strncat instead of snprintf, it works well.
Can someone explain why this code (snrpintf) works differently than I thought?

The number is a minimum.
In no case does a nonexistent or small field width cause truncation of a field; if the result of a conversion is wider than the field width, the field is expanded to contain the conversion result.
For %s, you can use the precision to specify a maximum
[An optional precision] gives [...] the maximum number of characters to be printed from a string for s and S conversions.
So use
snprintf(target, sizeof(target), "%3.3s%1.1s", src1, src2);
(No need for the target[4] = '\0'; with this!)

Related

How to printf a fixed length output from format

This following code:
printf("%d. %-10s:", 1, "Test");
produces this output:
1. Test :// 14 characters long
I would like the output length of the entire format `%d. %-10s:" to be exactly 10 characters like this:
1. Test: // 10 characters
Note:
The numbers vary in length, it could be 1 or 100, so I can't deduce it's length from the output.
How can I do that?
You need to use two steps:
char buffer[20];
snprintf(buffer, sizeof(buffer), "%d. %s:", 1, "Test");
printf("%-*s", 10, buffer);
The snprintf() operation give you a string 1. Test: in buffer; note that it includes the : in the output, and assumes no trailing blanks on the string "Test"). The printf() operation formats the string left justified (-) in a length of (at least) 10 (the * in the format and the 10 in the argument list) onto standard output. Presumably, something else will appear after this output on the same line; otherwise, there's no obvious point to the blank padding.
For full information, see:
snprintf()
This covers the basic operation of the *printf() family of functions (but does not list the interfaces to the v*printf() or *wprintf() families of functions).
The code in the question and in the answer above is all done with constants. A more realistic scenario would be:
void format_item(int number, const char *text, int width)
{
char buffer[width+1]; // C99 VLA
snprintf(buffer, sizeof(buffer), "%d. %s:", number, text);
printf("%-*s", width, buffer);
}
Note that this code truncates the formatted data if the number plus the string is too long. There are ways around that if you work a bit harder (like adding more than 1 to width in the definition of buffer — maybe add 15 instead of 1).
You might write:
format_item(1, "Test", 10);
or:
char *str_var = …some function call, perhaps…
int item = 56;
format_item(++item, str_var, 20);
etc.

sscanf function usage in c

I'm trying to parse xxxxxx(xxxxx) format string using sscanf as following:
sscanf(command, "%s(%s)", part1, part2)
but it seems like sscanf does not support this format and as a result, part1 actually contains the whole string.
anyone has experience with this please share...
Thank you
Converting your code into a program:
#include <stdio.h>
int main(void)
{
char part1[32];
char part2[32];
char command[32] = "xxxxx(yyyy)";
int n;
if ((n = sscanf(command, "%s(%s)", part1, part2)) != 2)
printf("Problem! n = %d\n", n);
else
printf("Part1 = <<%s>>; Part2 = <<%s>>\n", part1, part2);
return 0;
}
When run, it produces 'Problem! n = 1'.
This is because the first %s conversion specifier skips leading white space and then scans for 'non white-space' characters up to the next white space character (or, in this case, end of string).
You would need to use (negated) character classes or scansets to get the result you want:
#include <stdio.h>
int main(void)
{
char part1[32];
char part2[32];
char command[32] = "xxxxx(yyyy)";
int n;
if ((n = sscanf(command, "%31[^(](%31[^)])", part1, part2)) != 2)
printf("Problem! n = %d\n", n);
else
printf("Part1 = <<%s>>; Part2 = <<%s>>\n", part1, part2);
return 0;
}
This produces:
Part1 = <<xxxxx>>; Part2 = <<yyyy>>
Note the 31's in the format; they prevent overflows.
I'm wondering how does %31 works. Does it work as %s and prevent overflow or does it just prevent overflow?
With the given data, these two lines are equivalent and both safe enough:
if ((n = sscanf(command, "%31[^(](%31[^)])", part1, part2)) != 2)
if ((n = sscanf(command, "%[^(](%[^)])", part1, part2)) != 2)
The %[...] notation is a conversion specification; so is %31[...].
The C standard says:
Each conversion specification is introduced by the character %.
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).
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.
The 31 is an example of the (optional) maximum field width. The [...] part is a scanset, which could perhaps be regarded as a special case of the s conversion specifier. The %s conversion specifier is approximately equivalent to %[^ \t\n].
The 31 is one less than the length of the string; the null at the end is not counted in that length. Since part1 and part2 are each an array of 32 char, the %31[^(] or %31[^)] conversion specifiers prevent buffer overflows. If the first string of characters was more than 31 characters before the (, you'd get a return value of 1 because of a mismatch on the literal open parenthesis. Similarly, the second string would be limited to 31 characters, but you'd not easily be able to tell whether the ) was in the correct place or not.
If you know exactly how long are the parts of your "command", then the simplest option is:
sscanf(command, "%6s(%5s)", part1, part2);
This assumes that 'part1' is always 6 characters long and 'part2' is always 5 characters long (as in your code sample).
Try this instead:
#include <stdio.h>
int main(void)
{
char str1[20];
char str2[20];
sscanf("Hello(World!)", "%[^(](%[^)])", str1, str2);
printf("str1=\"%s\", str2=\"%s\"\n", str1, str2);
return 0;
}
Output (ideone):
str1="Hello", str2="World!"

How to append string using snprint() function

#include<stdio.h>
main()
{
char str[50] = "Wel %s";
char dst[50];
snprintf(dst,50,str,"Come");
//Now i want to append "*" to dst string ie "Wel Come*" using snprintf()
printf("str = %s\n",str);
printf("dst = %s\n",dst);
}
please suggest is it possible using snprintf()
Thanks
Surya
The obvious solution:
snprintf(dst,50,"%s*",dst);
is inefficient, because it makes an unnecessary copy of dst (into itself).
invokes undefined behavior as R. pointed out, because the arguments may not overlap (from man snprintf(3) on MacOSX):
"[...]or those routines that write to
a user-provided character string, that
string and the format strings should
not overlap, as the behavior is
undefined."
Posix says:
http://www.opengroup.org/onlinepubs/000095399/functions/printf.html
"If copying takes place between
objects that overlap as a result of a
call to sprintf() or snprintf(), the
results are undefined."
snprintf returns the number of characters it has written, so you can do this instead:
int k=snprintf(dst,50,str,"Come");
// make sure that we do not pass potential disastrous values to snprintf, because
// the size argument is unsigned (size_t, 50-52 is a large positive number!)
// and we want 50-k to be in the range 0-50
// k<0 means output error and k>50 means "output truncated". There is no point in
// appending anything in these cases anyway.
if (k<0 || k>50)
{
fprintf(stderr,"output error or buffer too small");
}
else k=snprintf(dst+k,50-k,"*");
// check k for truncation here.
And then there's always strcat...And just in case, you overlooked it. You can have the * attached right in the first place:
main()
{
char str[50] = "Wel %s*"; //<--!!!
[...]
This should work:
#include<stdio.h>
int main()
{
char str[50] = "Wel %s";
char dst[50];
int len;
snprintf(dst,50,str,"Come");
//get size of current string
len = strlen(dst);
//add character to the end
snprintf(dst + len, sizeof(dst) - len, "*");
printf("str = %s\n",str);
printf("dst = %s\n",dst);
return 0;
}
you can use the %s format for this:
snprintf(dst, 50, "%s*", dst);
EDIT: This seems to have some undefined behaviors. The best thing would be to ask if it is really necessary to use snprintf instead of strncat.
All the information is already available to you:
snprintf(dst + 8, sizeof(dst) - 8, "%s", "*");
You'd be better off doing:
strncat(dst, "*", sizeof(dst) - strlen(dst) - 1);

Limit Output in C

In C, I would like to limit the string to the first 8 characters. For example, I have:
char out = printf("%c", str);
How can I make it so it only returns the first 8 characters?
You can limit the length by setting the precision in the format specifier:
printf("%.8s", str);
This will print up to eight characters from the null-terminated string pointed-to by str. If the length of str is less than eight characters, then it will print the entire string.
Note that the format specifier for a null-terminated string is %s, not %c (%c is to print a single char), and that printf returns an int (the total number of characters printed), not a char.
No
That is incorrect. tabular printing "%8s" pads up to say 8 spaces, as in the example given. It does not truncate. ISOC99. If this is a windows only thing, okay, MS ignores the world on lots of things. If the length of the string is longer than the tabulation then the full string prints. See:
int main()
{
char tmp[]="123456789";
printf("1 %1s\n", tmp);
printf("2 %2s\n", tmp);
printf("4 %4s\n", tmp);
printf("8 %8s\n", tmp);
printf("16 %16s\n", tmp);
printf("32 %32s\n", tmp);
return 0;
}
output from gcc 3.4.2 on Solaris 5.9:
> ./a.out
1 123456789
2 123456789
4 123456789
8 123456789
16 123456789
32 123456789
sprintf() will duplicate and truncate a string then it can be sent to printf. Or if you don't care about the source string:
char * trunc(char *src, int len)
{
src[len]=0x0;
return src;
}
References: INTERNATIONAL STANDARD ©ISO/IEC ISO/IEC 9899:TC2, WG14/N1124 Committee Draft — May 6, 2005

How do I print a non-null-terminated string using printf?

How can I print a non-null-terminated string using printf, assuming that I know the length of the string at runtime?
printf("%.*s", length, string);
Use together with other args:
printf("integer=%d, string=%.*s, number=%f", integer, length, string, number);
// ^^^^ ^^^^^^^^^^^^^^
In C you could specify the maximum length to output with the %.123s format. This means the output length is at most 123 chars. The 123 could be replaced by *, so that the length will be taken from the argument of printf instead of hard-coded.
Note that this assumes the string does not contain any interior null bytes (\0), as %.123s only constrains the maximum length not the exact length, and strings are still treated as null-terminated.
If you want to print a non-null-terminated string with interior null, you cannot use a single printf. Use fwrite instead:
fwrite(string, 1, length, stdout);
See #M.S.Dousti's answer for detailed explanation.
The answer provided by #KennyTM is great, but with a subtlety.
In general, if the string is non-null "terminated", but has a null character in the middle, printf("%.*s", length, string); does not work as expected. This is because the %.*s format string asks printf to print a maximum of length characters, not exactly length characters.
I'd rather use the more general solution pointed out by #William Pursell in a comment under the OP:
fwrite(string, sizeof(char), length, stdout);
Here's a sample code:
#include <stdio.h>
int main(void) {
size_t length = 5;
char string[length];
string[0] = 'A';
string[1] = 'B';
string[2] = 0; // null character in the middle
string[3] = 'C';
string[4] = 'D';
printf("With printf: %.*s\n", length, string);
printf("With fwrite: ");
fwrite(string, sizeof(char), length, stdout);
printf("\n");
return 0;
}
Output:
With printf: AB
With fwrite: AB CD

Resources