Limit Output in C - 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

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.

How to parse an input line with sscanf?

I have an input .txt file that looks like this:
Robert Hill 53000 5
Amanda Trapp 89000 3
Jonathan Nguyen 93000 3
Mary Lou Gilley 17000 1 // Note that came contains of 3 parts!
Warren Rexroad 72000 7
I need to read those lines and parse them into three different categories: name (which is an array of chars), mileage (int) and years(int).
sscanf(line, "%[^] %d %d ", name, &mileage, &years);
This doesn't work very well for me, any suggestions?
THE PROBLEM
The problem with the current specifier passed to sscanf is that it is both ill-formed, and even when fixed it won't do what you want. If you would have used [^ ] as the first conversion specifier, sscanf would try to read as many characters as it can before hitting a space.
If we assume that a name can't contain digits specifying [^0123456789] will read the correct data, but it will also include the trailing space after the name, but before the first mileage entry. This is however easily solved by replacing the last space with a null-byte in name.
To get the number of characters read into name we can use the %n specifier to denote that we'd sscanf to store the number of bytes read into our matching argument; we can later use this value to correctly "trim" our buffer.
We should also specify a maximum width of the characters read by %[^0123456789] so that it doesn't cause a buffer-overflow, this is done by specifying the size of our buffer directly after our %.
SAMPLE IMPLEMENTATION
#include <stdio.h>
#include <string.h>
int
main (int argc, char *argv[])
{
char const * line = "Mary Lou Gilley 17000 1";
char name[255];
int mileage, years, name_length;
sscanf(line, "%254[^0123456789]%n %d %d ", name, &name_length, &mileage, &years);
name[name_length-1] = '\0';
printf ("data: '%s', %d, %d", name, mileage, years);
return 0;
}
data: 'Mary Lou Gilley', 17000, 1
If you have a function that finds the positon of the first digit like so:
// This function returns the position of the
// space before the first digit (assuming that
// the names dont contain digits)...
char *digitPos(char *s){
if isdigit(*(s+1)) return s;
else return digitPos(s+1);
}
You can then just separate the two variables by inserting a '\0' at the right position like so:
pos = digitPos(line); // This is a pointer to the space
*pos = '\0';
strcpy(name, line);
sscanf(pos + 1, "%d %d", &mileage, &years);
This might help you get started. It lacks the intelligence of BLUEPIXY's solution which handles the trailing whitespace a little better than mine ( or you could chop it off yourself).
dan#rachel ~ $ echogcc -o t t.c
dan#rachel ~ $ echo "Dan P F 3 21" | ./t
Name: Dan P F ,
Mileage: 3,
Years: 21.
Here's the code.
#include <stdio.h>
#include <string.h>
int main(){
char *buf;
int mileage, years;
while(!feof(stdin) ){
if( fscanf( stdin, "%m[^0-9] %d %d", &buf, &mileage, &years) == 3 ){
fprintf(stderr, "Name:\t %s,\nMileage:\t %d,\nYears:\t %d.\n",
buf, mileage, years
);
}
}
}
You have discovered one of the three reasons *scanf should never be used: it's almost impossible to write a format specification that handles nontrivial input syntax, especially if you have to worry about recovering from malformed input. But there are two even more important reasons:
Many input specifications, including your %[...] construct, are just as happy to overflow buffers as the infamous gets.
Numeric overflow provokes undefined behavior -- the C library is licensed to crash just because someone typed too many digits.
The correct way to parse lines like these is to scan for the first digit with strcspn("0123456789", line), or while (*p && !isdigit(*p)) p++;, then use strtoul to convert the numbers that follow.
int pos;
sscanf(line, "%*[^0-9]%n", &pos);
line[--pos]=';';
sscanf(line, "%[^;]; %d %d ", name, &mileage, &years);

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!"

C - can sscanf read from the same string it's writing to?

I have the following code
int myInt;
sscanf(str, "%d=%s", &myInt, str);
Will this be valid? Is there a better way to do this if I have it in a loop?
My guess is that this wil usually work because it seems like the source string will always be >= the result string, and that would seem to cause deterministic and as-specified results.
But I still wouldn't do it. Library functions typically have restrict-qualified parameters in order to allow for optimizations and prefetch.
Don't tempt the compiler.
You can utilize the %n conversion specification - chars read so far; int* parameter required. CStdLib.html#fscanf
Worth noticing. The standard say, about %n:
No input is consumed.
Corresponding argument shall be a pointer to signed integer.
Into which is written the number of characters read so far.
Does not increment the assignment count returned on completion.
If includes an assignment-suppressing character or a field width, the behavior
is undefined.
Ref. ISO/IEC 9899:1999 §7.19.6.2
As concept:
#include <stdio.h>
#include <string.h>
int main(void)
{
int n;
char *str = "12345=blabla 1313=blah "
"333=hello 343=goodbyeeeeeeeeeeeeeeeeeeeeeeeeeeee";
char buf[15];
int lt;
/* +----- limit read to buffer length -1
| +--- store read length here
| | */
while (sscanf(str, "%d=%14s%n", &n, buf, &lt) == 2) {
fprintf(stdout,
":: '%s' :: \n"
"Num: %d\n"
"Str: %s\n"
"Tot: %d bytes\n\n",
str,
n, buf,
lt);
str += lt;
}
return 0;
}
Should give something like (overlong %s input breaks loop):
:: '12345=blabla 1313=blah 333=hello 343=goodbyeeeeeeeeeeeeeeeeeeeeeeeeeeee' ::
Num: 12345
Str: blabla
Tot: 12 bytes
:: ' 1313=blah 333=hello 343=goodbyeeeeeeeeeeeeeeeeeeeeeeeeeeee' ::
Num: 1313
Str: blah
Tot: 10 bytes
:: ' 333=hello 343=goodbyeeeeeeeeeeeeeeeeeeeeeeeeeeee' ::
Num: 333
Str: hello
Tot: 10 bytes
:: ' 343=goodbyeeeeeeeeeeeeeeeeeeeeeeeeeeee' ::
Num: 343
Str: goodbyeeeeeeee
Tot: 19 bytes
How one would like to handle input longer then buffer could be many. I.e. check if eof str and if not re-alloc. Start out with buffer of length = str, etc.
Note that numbers > INT_MAX or < INT_MIN is undefined behavior (). Will (normally?) be truncated to INT_MAX or INT_MIN respectively when using the "%d" specification.
I.e.:
"1234533333333333333=blabla", read by "%d%s" =>
Num: 2147483647
Str: blabla
Tot: 26 bytes consumed
One way to tackle this is to use strtol etc., which, if number is > limit is defined to set value to MAX value for type and set errno = ERANGE. CStdLib.html#strtol

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