sscanf: Get a pointer to end position - c

I want to parse a const char *str string, and I need to read it by parts, with several calls to sscanf()
I have some example of what I could do if it was text of a file, which could be parsed with fscanf(), which updates the FILE *fp pointer to the position it stops reading:
fscanf(fp, "name %s ", name);
fscanf(fp, "date %*i ");
fscanf(fp, "{ ");
fscanf(fp, " isdst %*i ");
fscanf(fp, " yday %*i ");
fscanf(fp, " wday %*i ");
fscanf(fp, " year %i ", &year);
fscanf(fp, " mon %i ", &mon);
fscanf(fp, " mday %i ", &day);
But sscanf() doesn't update the pointer. Is there a way to do this?
EDIT:
From #pmg comment, I have this code now:
if (sscanf(str, " %lf%n", &a, &pos) != 1)
goto err;
str += pos;
if (sscanf(str, " %i%n", &b, &pos) != 1)
goto err;
str += pos;
which should be equivalent to a one line:
if (sscanf(str, " %lf %i", &a, &b) != 2)
goto err;
Is this what %n does?

Use "%n" conversion specifier in format string ... and corresponding variable.

Related

Reading from file does not work as expected with fscanf

I have this file:
0 -> 1:50 2:30 3:10
1 ->
2 -> 0:10 3:20
3 -> 1:20 2:10 3:30
And I want to extract all the numbers from the file with this code:
int a, b, c;
while (fscanf(fp, "%d ->", &a) == 1) {
printf("%d ->", a);
while (fscanf(fp, " %d:%d", &b, &c) == 2) {
printf(" %d:%d", b, c);
}
printf("\n");
}
The idea is that on the first loop it will scan the first number followed by space followed by -> and then the inner loop will scan the following sequence of " %d:%d" until the end of line which will then make the outer loop return 1 because it could successfully read "%d ->".
Ouput of code:
0 -> 1:50 2:30 3:10
It seems like it doesn't work as expected, and the outer while loop exits when it tried to read the number 1 from the second line(but it should?). I just find this weird because I've done something similar recently and it was working fine.
After removing the loops and replacing with this code I get the correct results so it had something to do with my loops:
fscanf(fp, "%d ->", &a);
fscanf(fp, " %d:%d", &b, &c);
fscanf(fp, " %d:%d", &b, &c);
fscanf(fp, " %d:%d", &b, &c);
fscanf(fp, "%d ->", &a);
fscanf(fp, "%d ->", &a);
fscanf(fp, " %d:%d", &b, &c);
fscanf(fp, " %d:%d", &b, &c);
fscanf(fp, "%d ->", &a);
fscanf(fp, " %d:%d", &b, &c);
fscanf(fp, " %d:%d", &b, &c);
fscanf(fp, " %d:%d", &b, &c);
An iteration of while (fscanf(fp, " %d:%d", &b, &c) == 2) { is consuming the "\n1" in "\n1 ->". This fouls the following fscanf(fp, "%d ->", &a), which starts at " ->" and stops the loop.
Code needs to detect the '\n'. format directives/specifiers like " " and "%d" consume end-of-line without reporting that.
Direct solution: As the file is lines of data, then read lines of text with fgets().
Use "%n" to record offset of scan.
// Generous maximum line size
#define LINE_N 1024
char line[LINE_N];
while (fgets(line, sizeof line, fp)) {
int a;
int n = 0;
// v--- space needed to consume trailing whitespace
sscanf(line, "%d -> %n", &a, &n) == 1) {
if (n == 0) {
fprintf(stderr, "Invalid line '%s'\n", line);
continue;
}
printf("%d ->", a);
char *p = line + n;
while (*p) {
int b,c;
n = 0;
// v--- space needed to consume trailing whitespace
sscanf(p, "%d:%d %n", &b, &c, &n);
if (n == 0) {
fprintf(stderr, "Invalid rest of line '%s'\n", p);
break;
}
printf(" %d:%d", b, c);
p += n;
}
printf("\n");
}
The algorithm isn't handling the newlines. So it gets stuck and the end of the first line and isn't able to read anything matching n ->.
One way that works reliably for all C RTL implementations is to use fgets() to read each whole line (including the newline at the end) and then use sscanf() to parse the string.
I think you just need to fseek one char ( newline ) at the last printf.

Why is this simple program crashing?

I basically have a structure with a student's information in it. After it takes the last input it crashes. The last printf is never presented and my compiler doesn't find any errors.
struct stud_prof {
char student_name[NAME_LIMIT];
char ssn_number[SSN_LIMIT];
double gpa;
int units;
char major_code;
} student1;
int main(void)
{
printf( "What is the student's name?\n" );
scanf(" %s", &student1.student_name);
fflush(stdin);
printf( "What is the student's Social Security number?\n" );
scanf(" %s", &student1.ssn_number);
fflush(stdin);
printf( "What is the student's GPA?\n" );
scanf(" %lf", &student1.gpa);
fflush(stdin);
printf( "How many units has the student completed?\n" );
scanf(" %d", &student1.units);
fflush(stdin);
printf( "Enter the student's Major Code.\n" );
scanf( " %s", &student1.major_code);
printf( " %s, %s, %f, %d, %s ", student1.student_name,
student1.ssn_number, student1.gpa, student1.units, student1.major_code);
return 0;
}
Be aware that fflush(stdin); is undefined behaviour, but it's most likely not the cause of the problem here.
This is wrong:
printf( "Enter the student's Major Code.\n" );
scanf( " %s", &student1.major_code);
printf( " %s, %s, %f, %d, %s ", student1.student_name,
student1.ssn_number, student1.gpa, student1.units, student1.major_code);
The format specifiers don't match the arguments.
You are using %s for student1.major_code which is a char and not a char*.
Use %c instead:
printf( "Enter the student's Major Code.\n" );
scanf( " %c", &student1.major_code);
printf( " %s, %s, %f, %d, %c", student1.student_name,
student1.ssn_number, student1.gpa, student1.units, student1.major_code);

Error message in C invalid binary

I'm getting an error:
Type invalid operands to binary & (have 'int *' and 'int')
here is my program. The problem occurs at line 34 or the fscanf num1
#include <stdio.h>
#include <stdlib.h>
FILE *infile;
FILE *prnt;
main()
{
int num1, num2, nums;
char complex;
float fcost;
char name [11];
infile = fopen ("F:/DATA.txt", "r");
prnt = fopen ("F:/income.txt", "w");
if (infile == 0)
{
printf ("FILE NOT ON DISK\n");
system("pause");
return 0;
}
fprintf (prnt, "%-15s %-23s %6s\n\n", "ABAHLMAN", "Program 1", "PAGE 1");
fprintf (prnt, "\n");
fscanf (infile, " %i %i %i %f %c", &nums &num1 &num2 &fcost &name);
while (!feof (infile))
{
int area = (nums * 200) + (num1 * 300) + (num2 * 450);
float cost = fcost + (area * 75.00);
double income = 12 * ((nums *450) + (num1 * 550) + (num2 *700));
float payback = cost/ income;
fprintf (prnt, "%-10s %5f %7c %9.2f\n", name, payback, area, cost);
fscanf (infile, " %d %d %d %f %c", &nums &num1 &num2 &fcost &name);
}
fclose (infile);
fclose (prnt);
return 0;
}
You are not separating the arguments with comma in fscanf() statements. They should be:
fscanf (infile, " %i %i %i %f %s", &nums, &num1, &num2, &fcost, name);
and
fscanf (infile, " %d %d %d %f %s", &nums, &num1, &num2, &fcost, name);
Note that name is an array and it gets converted into a pointer when you pass it to fscanf(). So, the & operator needs to be dropped. As noted in the comments, the format should be %c for name.
Also, see: What is array decaying?
I'd also suggest to use a standard definition for main() function. main() {..} is outdated and should be avoided.
Instead, you can write it as int main(int).
I see a few problems. First, no commas to separate the arguments for scanf.
Next, the last argument for fscanf should have %s format and be passed name without &.
Next, feof is not the way to control a loop, you should check the return value from fscanf anyway.
You also use the wrong format specifier %c for area in printf which should be %d
Finally be consisent with the use of %d or %i format specifier. Unless you want the user to input in other number bases than decimal, stick to %d.
So I suggest the loop should be
while (fscanf (infile, " %d %d %d %f %s", &nums, &num1, &num2, &fcost, name) == 5)
{
int area = nums * 200 + num1 * 300 + num2 * 450;
float cost = fcost + area * 75.00;
double income = 12 * (nums * 450 + num1 * 550 + num2 * 700);
float payback = cost / income;
fprintf (prnt, "%-10s %5f %7d %9.2f\n", name, payback, area, cost);
}

Take sentence from user input with C

I wana take input from user and then print, I think I should also allocate memory could someone show me how to do that properly?
Here is my try:
int days = 1;
char * obligation[1500];
char * dodatno[1500];
puts("Enter nuber of days till obligation:\n");
scanf(" %d", &days);
puts("Enter obligation:\n");
scanf(" %s", obligation);
puts("Sati:\n");
scanf(" %s", dodatno);
printf("%s|%s|%s \n",days,obligation,dodatno);
You don't need to allocate dynamically. Just store it in a char array.
And printf("%s|%s|%s.... is wrong in your case, change the first %s to %d
Here is an example of how that would look like
int main()
{
int days = 1;
char obligation[256];
char dodatno[256];
printf("Enter number of days till obligation: ");
scanf("%d", &days);
printf("Enter obligation: ");
scanf("%s", obligation);
printf("Sati: ");
scanf("%s", dodatno);
printf("%d | %s | %s", days, obligation, dodatno);
return 0;
}
Otherwise, if you want to read in the whole line, you could use fgets and parse using strtok:
char* days;
char* obligation;
char* dodatno;
char line[256];
printf("Enter days, obligation and sati: ");
fgets(line, sizeof(line), stdin);
days = strtok(line, " ");
obligation = strtok(NULL, " ");
dodatno = strtok(NULL, "\n");
printf("%s | %s | %s", days, obligation, dodatno);
return 0;

C: How can I store an unknown number of inputs using scanf so that each word of the input is stored in a different variable?

So far i have:
char r[4][10];
printf("Enter an option: \n");
scanf("%s %s %s %s %s", r[0],r[1],r[2], r[3], r[4]);
printf("%s %s %s %s %s\n", r[0], r[1], r[2], r[3], r[4]);
This works as long as 5 inputs are given. Right now if you type less then 5 inputs and hit enter the program continues to ask for input until it has all 5 values but I want the scanf to stop and the program to continue if less then 5 inputs are entered.
You'll do best to read a line with fgets() and parse it with sscanf(). If there will be no more than 5 words on a line:
char r[5][10];
char line[4096];
printf("Enter an option: \n");
if (fgets(line, sizeof(line), stdin) != 0)
{
int num = sscanf(line, "%9s %9s %9s %9s %9s", r[0],r[1],r[2], r[3], r[4]);
for (int i = num; i < 5; i++)
r[i][0] = '\0';
printf("%s %s %s %s %s\n", r[0], r[1], r[2], r[3], r[4]);
}
Note that you only had r[4][10] but were using subscript 4 — bad move.
A better way to handle it would be a loop that scans over the line, reading a word at a time until there is no more space in the array or no more words in the line. It's a little trickier, though.

Resources