Multiple sprintf calls to format a string piece by piece - c

I have a problem wherein I want to call sprintf(or similar function) multiple times for it to replace the format specifier with the data values one at a time,
For E.g.: char *str = "Temperature is %f, Humidity is %d, wait for %d secs";
Now I receive the argument data in multiple variables and I want to be able to call
float temp = 23.45;
char humidity = 87;
int time = 3;
sprintf(buf, str, temp);
sprintf(buf, str, humidity);
sprintf(buf, str, time);`
However we see the first call to sprintf itself will replace first %f with 23.45 and other format specifiers with garbage or 0. What I expect in str after first sprintf call is
"Temperature is 23.45, Humidity is %d, wait for %d secs"
So I know I cannot use sprintf or snprintf for this, is there another string library call which does what I a expecting?
I tried this in online gdb and sprintf is not the function I am looking for.

It seems to me like you probably want to call
sprintf(buf, str, temp, humidity, time);
This should result in "Temperature is 23.45, Humidity is 87, wait for 3 secs", all in one go.
However, if calling sprintf multiple times is important to you, for some reason, you can simply escape the percent signs an appropriate number of times, so that each sprintf call evaluates one more escape. By which I mean,
char *str = "Temperature is %f, Humidity is %%d, wait for %%%%d secs";
float temp = 23.45;
char humidity = 87;
int time = 3;
sprintf(buf, str, temp); // "Temperature is 23.45, Humidity is %d, wait for %%d secs"
sprintf(buf2, buf, humidity); // "Temperature is 23.45, Humidity is 87, wait for %d secs"
sprintf(buf3, buf2, time); // "Temperature is 23.45, Humidity is 87, wait for 3 secs"

You CAN use sprintf().
Try something like this:
float temp = 23.45;
char humidity = 87;
int time = 3;
char *at = buf;
at += sprintf( at, "Temp: %.1f ", temp );
at += sprintf( at, "Humidity: %d ", (int)humidity );
at += sprintf( at, "Time: %d", time );
puts( buf );
It's up to you to make sure that buf[] is large enough to hold the entire string.
It's a nice feature of this simplicity that the 'humidity' field (or another) could be easily suppressed with an appropriate if().

You can use %n to track output length. You also must separate the format string into its constituent parts.
int n1, n2;
sprintf( buf, "Temperature is %f, %n", temp, &n1 );
sprintf( buf+n1, "Humidity is %d, %n", humidity, &n2 );
sprintf( buf+n1+n2, "wait for %d secs", time );
You could also just use strchr():
*buf = 0;
sprintf( strchr( buf, 0 ), "Temperature is %f, ", temp );
sprintf( strchr( buf, 0 ), "Humidity is %d, ", humidity );
sprintf( strchr( buf, 0 ), "wait for %d secs", time );
Honestly, though, Fe2O3’s answer is simplest, heh. 😉

Related

_snwprintf_s heap corruption error in free() call

I am trying to use _snwprintf_s to concatenate two strings. I also want to append \r\n after each string.
So I allocate a wchar buffer initially including the \r\n and null for both strings. I try to print one after the other.
I see both strings are written. But when trying to free(wbuff), it throws me a "Heap corruption" error. I am not able to figure where I am crossing the bounds.
Where am I going wrong? Let me know. Thanks
int main()
{
WCHAR* name1 = L"HelloWorld";
WCHAR* name2 = L"GoodMorning";
WCHAR* wbuff = NULL;
int i = wcslen(name1) + wcslen(name2) + 6; //in words for size of buffer
int out = 0;
wbuff = (WCHAR*)malloc(i * sizeof(WCHAR));
ZeroMemory(wbuff, i * sizeof(WCHAR));
int prevLen = 0, currLen = 0;
currLen = wcslen(name1) + 2; //in bytes
out = _snwprintf_s(wbuff,i,currLen, L"%s\r\n", name1);
printf("Wrote %d characters\n", out);
prevLen = currLen;
currLen = wcslen(name2) + 2;
out = _snwprintf_s((wbuff+prevLen),i,currLen, L"%s\r\n", name2);
printf("Wrote %d characters\n", out);
printf("%S of sisze %u", wbuff, wcslen(wbuff));
free(wbuff);
printf("memory freed\n");
}
_snwprintf_s fills up the remaining characters in the buffer with an fefe marker. I can't find this in the documentation, but I can see it in the debugger. Hence, your second _snwprintf_s is indicating it's got i characters available, but your start position is prevLen chars into it. Fix i to have the offset adjustment as well.
Instead of this:
out = _snwprintf_s((wbuff+prevLen),i,currLen, L"%s\r\n", name2);
This:
out = _snwprintf_s((wbuff+prevLen),i-prevLen,currLen, L"%s\r\n", name2);
_snwprintf_s will fill up the entire buffer with markers
Instead of this:
printf("%S of sisze %u", wbuff, wcslen(wbuff));
This:
printf("%ls of sisze %u", wbuff, (unsigned int)(wcslen(wbuff)));

How can i ignore the rest of the input in C?

I am making a program that needs to receive the following input:
{Time1 Time2 Appointment} as {hour1:minute1 hour2:minute2 Appointment}
The "Appointment" is a useless string input and i would like to ignore it
scanf("%d:%d %d:%d",&hour1,&minute1,&hour2,&minute2);
I am able to get the hours and minutes but after that i can't use "scanf" again, how can i just ignore the "Appointment" input?
You can do %*s thing, and also you can use an input buffer after the first scanf. Something like this:
int minute1 = 0, hour1 = 0, minute2 = 0, hour2 = 0;
char buffer[100];
scanf("%d:%d %d:%d %99[^\n]s",&hour1, &minute1 ,&hour2 ,&minute2, buffer);
printf("%d:%d %d:%d\n", hour1, minute1, hour2, minute2);

Why can't I read values into simple variables with simple format string using sscanf() in C?

I am trying to parse some simply formatted text and create a simple data structure of text/numeric records using numeric and short string values. I have debugged and located the problem; it's down to sscanf() not reading the values into my variables using a specific format string (other format strings in the program work well). I have created a simple text file to see what's happening.
Code is as follows:
char *idNumber = (char *)malloc(sizeof (char*));
char *partNumber = (char *)malloc(sizeof (char*));
int amountItems = 0;
double unitPrice = 0;
char *line1 = "Govan, Guthrie (N210) AX299 x 6 $149.94";
char *line2 = "Mustaine, Dave (N106) AX350N x 2 $63.98";
char *line3 = "Van Halen, Edward (N1402) AV2814 x 10 $34.90";
sscanf(line1, "%*s, %*s (%s) %s x %d $%lf", idNumber, partNumber,
&amountItems, &unitPrice);
printf("%s, %s, %d, %f\n", idNumber, partNumber, amountItems, unitPrice);
sscanf(line2, "%*s, %*s (%s) %s x %d $%lf", idNumber, partNumber,
&amountItems, &unitPrice);
printf("%s, %s, %d, %lf\n", idNumber, partNumber, amountItems, unitPrice);
sscanf(line3, "%*s, %*s (%s) %s x %d $%lf", idNumber, partNumber,
&amountItems, &unitPrice);
printf("%s, %s, %d, %lf\n", idNumber, partNumber, amountItems, unitPrice);
I am interested in the following fields, with the rest being ignored. For instance, in record:
"Govan, Guthrie (N210) AX299 x 6 $149.94"
I want N210, AX299, 6, and 149.94 in my variables, in that order.
Result is as follows:
andrew#levin-Inspiron-3650:~/Desktop/schoolwork/project2$ ./a.out
, , 0, 0.000000
, , 0, 0.000000
, , 0, 0.000000
Expected output is:
N210, AX299, 6, 149.94
N106, AX350N, 2, 63.98
N1402, AV2814, 10, 34.90
Please share help!
This is not code directly from my program but a "helper" file I created on the side just to debug this one issue very simply without having to invoke the entire application!
The following similar code worked well for a different format:
Record being:
N210 AX299 6 24.99
in following code:
struct record *current = malloc(sizeof(struct record *));
current->idNumber = (char *)malloc(sizeof (char *) * 8);
current->partNumber = (char *)malloc(sizeof (char *) * 10);
sscanf(line, "%s %s %d %lf", current->idNumber, current->partNumber,
&(current->amountItems), &(current->unitPrice));
I do not expect this code to be a wealth of C beauty, I am a Java developer this is a C project for community college. But can you help me debug this one sscanf problem.
Thank you!
There is a problem with the dynamic allocation here. The line char *idNumber = (char *)malloc(sizeof (char*)); allocates space for a pointer to char, not for a char, or an array of chars. This should be something like:
char *idNumber = malloc(sizeof (char) * 256);
or:
char *idNumber = malloc(sizeof *idNumber * 256);
Note that there is no need to cast the result of malloc() in C. The second version is a very idiomatic way to do this in C. By avoiding use of an explicit type in the operand to sizeof, this is easier and less error-prone in the coding, and easier to maintain when types change during the life of the code. But, since sizeof (char) is always 1 in C, this might as well be:
char *idNumber = malloc(256);
No point in being stingy with allocations, and 256 gives plenty of space for input. And please remember to always check that allocation was successful before attempting to use allocated memory; and don't forget to free malloced memory when finished with it.
But, this is not causing the trouble. The problem is that the format string tells sscanf() to match a comma after the first string, yet in the input this comma is consumed by %*s. There is no further match, so sscanf() returns. There is a further problem with %s) consuming the ) at the end of the input string, leaving no closing parenth to match in the format string. And the %s conversion specifier reads strings up to a whitespace character, so "Van Halen," is consumed by %*s %*s, leaving "Edward" for an attempted match with (%s). These sorts of errors are detectable; one should always check the value returned by calls to scanf() family functions to be certain that input is as expected.
The scanset directive can be used to good effect here. This directive: %*[^(]( tells scanf() to match any characters until a ( is encountered, suppressing assignment, and matching the ( in the end before continuing. Then the %255[^)]) directive tells scanf() to match up to 255 characters, until a ) is encountered, storing the results in an array, and matching the ) in the end before continuing. Note the specification of a maximum width here to prevent buffer overflow, and note that room must be left for the \0 terminator which will always be added by scanf().
Here is a modified program which will work as expected:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *idNumber = malloc(256);
if (idNumber == NULL) {
perror("Allocation failure");
exit(EXIT_FAILURE);
}
char *partNumber = malloc(256);
if (idNumber == NULL) {
perror("Allocation failure");
exit(EXIT_FAILURE);
}
int amountItems = 0;
double unitPrice = 0;
char *line1 = "Govan, Guthrie (N210) AX299 x 6 $149.94";
char *line2 = "Mustaine, Dave (N106) AX350N x 2 $63.98";
char *line3 = "Van Halen, Edward (N1402) AV2814 x 10 $34.90";
if (sscanf(line1, "%*[^(]( %255[^)]) %255s x %d $%lf",
idNumber, partNumber, &amountItems, &unitPrice) < 4) {
fprintf(stderr, "Input error in line1\n");
} else {
printf("%s, %s, %d, %f\n",
idNumber, partNumber, amountItems, unitPrice);
}
if (sscanf(line2, "%*[^(]( %255[^)]) %s x %d $%lf",
idNumber, partNumber, &amountItems, &unitPrice) < 4) {
fprintf(stderr, "Input error in line2\n");
} else {
printf("%s, %s, %d, %f\n",
idNumber, partNumber, amountItems, unitPrice);
}
if (sscanf(line3, "%*[^(]( %255[^)]) %s x %d $%lf",
idNumber, partNumber, &amountItems, &unitPrice) < 4) {
fprintf(stderr, "Input error in line3\n");
} else {
printf("%s, %s, %d, %f\n",
idNumber, partNumber, amountItems, unitPrice);
}
free(idNumber);
free(partNumber);
return 0;
}
Program output:
N210, AX299, 6, 149.940000
N106, AX350N, 2, 63.980000
N1402, AV2814, 10, 34.900000
Your format strings simply do not match the argument types. Rather than going one-by-one trying to point out every error here, the way to prevent this is to enable compiler warnings. For example if you're using GCC or Clang, add -Wall -Wextra -Werror to your compiler command. Then, the compiler will tell you everything you need to know about format string mismatches.

Safely printing all attributes of struct in C

I'm writing a toString method that prints out all attributes of an inputted pointer to a struct. In reading safe methods for handling strings, I finally created the below solution:
note a Person struct has attributes name, weight, height, and age (all ints except for name, which is a char array).
char* toString(struct Person* inputPerson)
{
// Allocate memory to return string
char* ret = malloc(sizeof (char) * 100);
// copy "Name: " into string
strcpy(ret, "Name: ");
// safely copy name, at most leaving enough room for the other params (leaving 50 bytes)
strncat(ret, inputPerson->name, 100-50);
// copy "Age: " into string
strcat(ret, "\n\tAge: ");
// create tmp char to allow us to convert ints age, weight, and height into strings
char tmp[4];
// safely convert string to int (max number of digits being 3)
snprintf(tmp, sizeof(tmp), "%d", inputPerson->age);
// safely copy at most 3 characters, plus a null terminating char
strcat(ret, tmp); // the previous snprintf makes sure that tmp is not too large.
// repeat previous 2 steps for both weight and height attributes
strcat(ret, "\n\tWeight: ");
snprintf(tmp, sizeof(tmp), "%d", inputPerson->weight);
strcat(ret, tmp);
strcat(ret, "\n\tHeight: ");
snprintf(tmp, sizeof(tmp), "%d", inputPerson->height);
strcat(ret, tmp);
// Return a pointer to the string
return ret;
}
My question is: is this overkill? All I want to do is print out each attribute safely and securely. For each string, I have to make sure it is a maximum size before appending. For each integer, I have to snprintf it into a string (ensuring a maximum allowed length) and then append that string to my return string. Is there a simpler way? Also my heebie jeebies are growing every time I look at the "100-50" code portion: how can I specify "size allocated to ret" instead of 100?
Since you are mallocing the return buffer anyway, why not make life easier on yourself by allocating a buffer of the correct size? As Joachim suggests, you can even do that with snprintf:
char* toString(struct Person* inputPerson)
{
size_t space_required =
snprintf(0, 0,
"Name: %s\n"
"\tAge: %d\n"
"\tWeight: %d\n"
"\tHeight: %d\n",
inputPerson->name,
inputPerson->age,
inputPerson->weight,
inputPerson->height);
// space_required excludes the terminating NUL
// sizeof(char) == 1 *by definition*
char *ret = malloc(space_required+1);
if (!ret)
return 0;
snprintf(ret, space_required+1,
"Name: %s\n"
"\tAge: %d\n"
"\tWeight: %d\n"
"\tHeight: %d\n",
inputPerson->name,
inputPerson->age,
inputPerson->weight,
inputPerson->height);
return ret;
}
That probably seems awful repetitive. If your C library has asprintf, you can avoid the repetition:
char* toString(struct Person* inputPerson)
{
char *ret;
if (asprintf(&ret,
"Name: %s\n"
"\tAge: %d\n"
"\tWeight: %d\n"
"\tHeight: %d\n",
inputPerson->name,
inputPerson->age,
inputPerson->weight,
inputPerson->height) == -1)
return 0;
return ret;
}
If you don't have asprintf, you can implement it using malloc and vsnprintf. I'll leave that as an exercise ;-)

Storing a char in an array segfault

I'm getting a segfault in my code and I'm not sure why. I read through a file and count the number of lines in order to dynamically allocate my arrays. I then rewind the file, read the data in the file, storing the data into variables, and then storing the read variables into arrays, but I'm having trouble with chars.
...
char *aname = malloc(sizeof(char) * 3);
...
// get # lines in file (count)
...
char *aname_seen = malloc(count * (sizeof(char) * 3));
...
rewind(file);
while (fgets(buff, sizeof buff, file) != NULL)
{
if (sscanf(buff, "%s %d %s %s %d %lf %lf %lf %lf %lf\n",
atm, &serial, aname, resName, &resSeq, &x, &y, &z,
&occupancy, &tempFactor) == 10)
{
aname_seen[i] = *aname;
printf("%d: %s vs %s\n", i, aname, aname_seen[i]);
i++;
} // end sscanf if-loop
} // end while loop
I can print aname with printf("%d: %s\n", i, aname) and get the expected output, but I'm getting Segmentation fault (core dumped) when I try printf("%d: %s vs %s\n", i, aname, aname_seen[i]).
This while loop + nested if loop is the same convention I use to count the number of lines, so i will increment up to count. Am I incorrectly allocating aname_seen and not actually giving it count number of char*3 elements? I'm not well versed in messing with char's. More of a numerical fortran buff, so I need some direction.
Thanks in advance!
The %s format specifier is supposed to correspond to a char * argument. In your case, aname_seen[i] is a char, which gets promoted to an int for the purpose of passing to a variadic function (printf). An int is not a char *.
Perhaps you meant one of these:
printf("%d: %s vs %c\n", i, aname, aname_seen[i]);
printf("%d: %s vs %s\n", i, aname, &aname_seen[i]);
If neither of these solve your problem, please explain precisely the behaviour you expect from this expression and give us a minimal, compilable testcase. Your current testcase isn't compilable.
you way you defined aname_seen is a pointer to a char array
char *aname_seen = malloc(count * (sizeof(char) * 3));
so aname_seen[i] is a char
so the
printf("%d: %s vs %s\n", i, aname, aname_seen[i]);
should be
printf("%d: %s vs %c\n", i, aname, aname_seen[i]);

Resources