How to read lines with different formats - c

I have an input file with the following form
i 176064 Patterson Denise 8.58 11 DEN 15788
q 188464
ra 148702 167443
a 73131
d 163464
f 6.00
ct 73131 PHY
b 3
p 15703
pe
m 144626 6.51 8
e
The first character in each line[i,q,ra,a...] represents an code to a function , while the rest are values that I must store into variables,depending on that code. What's the best way to achieve this ? I have been thinking about using fscanf but each line does not have a specific format, the format itself depends on the code [i,q,ra,a,b..]

To read a line, use fgets()
char buffer[100];
while (fgets, buffer, sizeof buffer, istream) != NULL) {
Then scan the line against the various formats, each ending with " %n". "%n" records the scan position, if it got that far. Additional tests could check for extraneous extras character starting at n.
int num1, num2, num3;
char last[sizeof buf];
char first[sizeof buf];
char code[sizeof buf];
double rate;
int n = 0;
// v..v..v..v...v..v..v spaces optional here
sscanf(buffer, "i %d %s %s %lf %d %s %d %n",
&num1, last, first, &rate, &num2, code, &num3, &n);
if (n) {
Handle_i();
continue;
}
sscanf(buffer, "q %d %n", &num1, &n);
if (n) {
Handle_q();
continue;
}
sscanf(buffer, "ra %d %n", &num1, &num2, &n);
if (n) {
Handle_ra();
continue;
}
sscanf(buffer, "e %n", &n);
if (n) {
Handle_e();
continue;
}
...
fail();
}
As each format begins with a unique letter pattern, the sscanf() will quickly exit on mis-match.
Alternative, code could parse out the initial letters for a slightly more efficient decision tree. Suspect profiling will show little performance difference.
As with any complex format, consider how one would maintain the code and it is bound to evolve.

Related

Can you use 'fscanf' to read in individual chars and an int?

I am trying to convert some code that reads in three integers per line of txt file til end of file.
ie:
int int int
int int int
int int int
What I am trying to get it to do now is read in is:
z z 5
z z 6
z z 5
til end of file.
I'm having an issue right after it opens the file. I placed several debuggers to track the progress and I commented what is printed. It only shows the before and after of the first line of the file. So I'm guessing it is only reading one line.
int i,origin,destin,wt;
char a, b;
int nodes = 0;
printf("before file is opened\n"); // displayed
FILE *fptr = fopen("input.txt","r");
printf("after file is opened\n"); // displayed
while (fscanf(fptr, "%c %c %d", &a, &b, &wt) == 3){
printf("\before is : %c %c\n", a, b); // shows the first line of file
origin = convertToNum(a);
destin = convertToNum(b);
printf("\nconversion is : %d %d\n", origin, destin); // 1st line of file shown
}
edit
Changing:
while (fscanf(fptr, "%c %c %d", &a, &b, &wt) == 3)
to:
while (fscanf(fptr, "%c %c %d", &a, &b, &wt) != EOF)
Produces slightly better results. It will read in one line just fine.
Every other line seems to have a whitespace issue.
I didn't realize %c also grabbed newline characters.
char buf[512];
while (fgets(buf, sizeof buf, fptr) != 0) {
sscanf(buf, "%c %c %d", &a, &b, &wt);

Extract multiple integers from a string in C

I'm new to programming and I'm stuck at this basic problem: I have a string as input and it can contain things like 1,2c, 11,120p, 5u so basically 1 or 2 integers (if two, they are separated by ,) and a character at the end. I have to save the integers and the ending character in different variables, but I don't know how to do it and how to separate the case with 1 integer with the case with 2. Thanks for the answers.
You can use sscanf() to try and parse the string and check how many conversions succeeded:
#include <stdio.h>
void parse_string(const char *s) {
int n1, n2;
char c1, c2;
if (sscanf(s, "%d,%d%c%c", &n1, &n2, &c1, &c2) == 3) {
printf("2 numbers: %d and %d, character is %c\n", n1, n2, c1);
} else
if (sscanf(s, "%d%c%c", &n1, &c1, &c2) == 2) {
printf("1 number: %d, character is %c\n", n1, c1);
} else {
printf("invalid string format: %s\n", s);
}
}
In the above code, the string will be rejected if it does not match one of the formats, but also if there is a trailing character after the match, including a space. Note however that the match might not be strict enough as embedded spaces and a + or - sign in front of the numbers will also be accepted. The Standard C library does not include a more general pattern matching mechanism. An initial test such as if (sscanf(s, "%*[0-9],%*[0-9]%c%c", &c1, &c2) == 1) could be used to exclude spaces and signs but numbers exceeding the range of int would still pass this preliminary test and cause undefined behavior for the subsequent %d conversion.
You can use substring for this problem .
https://www.programmingsimplified.com/c/source-code/c-substring
int main()
{
char string[1000], sub[1000];
int position, length, c = 0;
printf("Input a string\n");
gets(string);
printf("Enter the position and length of substring\n");
scanf("%d%d", &position, &length);
while (c < length) {
sub[c] = string[position+c-1];
c++;
}
sub[c] = '\0';
printf("Required substring is \"%s\"\n", sub); // '\"' to print "
return 0;
}

How to read an unknown quantity of integers from console?

I have entries like these:
0 5 260
1 0 -598
1 5 1508
2 1 -1170
I don't know previously how many (console) inputs I'll get, so I have to read until there are no entries left.
I started with a code like this:
int a, b, c;
while(scanf("%d %d %d", &a, &b, &c)!=EOF){
// do stuff here
}
But it never stops asking for new input.
Then, I saw people in other threads suggesting this:
int a, b, c;
while(scanf("%d %d %d", &a, &b, &c)==1){
// do stuff here
}
In this case, it doesn't even enter the while.
Does anyone know what I'm doing wrong?
An approach: Continue asking for input until the input is closed (EOF) or some problem is encountered. (Invalid line of input)
The below uses fgets() to read a line.
Then, " %n" to detect where scanning stopped. If scanning does not reach %n, n will still have the value of 0. Otherwise it gets the offset in buffer where scanning stopped, hopefully it was at the null character '\0'.
char buffer[100];
while (fgets(buffer, sizeof buffer, stdin)) {
int n = 0;
sscanf(buffer, "%d%d%d %n", &a, &b, &c, &n);
if (n == 0) {
fprintf(stderr, "3 int were not entered\n");
break;
}
if (buffer[n] != 0) {
fprintf(stderr, "Extra input detected.\n");
break;
}
// do stuff here with a,b,c
}
There are many approaches to solve this issue.
while(scanf("%d %d %d", &a, &b, &c)==1)
means that "if scanf() successfully read just one value, proceed in the loop."
Therefore, if you enter something like 0 junk, the scanf() read just 1 data and will enter the loop once.
Try using
while(scanf("%d %d %d", &a, &b, &c)==3)
to have it enter the loop when scanf() successfully read three values, which is what expected.

Validate Input to a C Program

i have a c program in which i am accepting 2 numbers as input.
How do i validate if input entered is numbers only and not characters.
void main()
{
int a,b;
printf("Enter two numbers :");
scanf("%d%d",&a,&b);
printf("Number 1 is : %d \n Number 2 is : %d",a,b);
}
[Edit] Added Sample Code
Besides the other interesting suggestions (especially the one with scanf), you might also want to use the isdigit function:
The isdigit() function shall test
whether c is a character of class
digit in the program's current locale
note that this function examines only ONE char, not an entire bunch of them.
It is always good practice to resort to already-built functions; there are intricacies you might not be aware of even in the simplest task, and this will make you a good programmer overall.
Of course, in due time you might want to look at how that function works to get a good grasp of the underlying logic.
scanf returns the number of items that it has successfully scanned. If you asked for two integers with %d%d, and scanf returns 2, then it successfully scanned both numbers. Any number less than two indicates that scanf was unable to scan two numbers.
int main()
{
int a,b;
int result;
printf("Enter two numbers :");
result = scanf("%d%d",&a,&b);
if (result == 2)
{
printf("Number 1 is : %d \n Number 2 is : %d",a,b);
}
else if (result == 1)
{
// scanf only managed to scan something into "a", but not "b"
printf("Number 1 is : %d \n Number 2 is invalid.\n", a);
}
else if (result == 0)
{
// scanf could not scan any number at all, both "a" and "b" are invalid.
printf("scanf was not able to scan the input for numbers.");
}
}
One other value that scanf may return is EOF. It may return this if there is an error reading from the stream.
Also note that main returns int, but you have it declared with void return.
Read user line of text input as a string. This greatly simplifies error handling.
int a = 0, b = 0;
char buf[100];
for (;;) {
printf("Enter two integers :");
if (fgets(buf, sizeof buf, stdin) == NULL) {
printf("Input closed\n");
break;
}
Then test the string for 2 ints with no following junk. Use sscanf() (simple) , strtol() (more robust), etc.
int n; // %n records where scanning stopped
if (sscanf(buf, "%d%d %n", &a, &b, &n) == 2 && buf[n] == '\0') {
printf("Number 1 is : %d \n Number 2 is : %d", a, b);
break;
}
printf("<%s> is not 2 integers. Try again\n", buf);
}
More advanced code uses strtol() to validate and also detect excessively long lines of input.

Converting a string of numbers into integers

I have a string (char) and I want to extract numbers out of it.
So I have string: 1 2 3 4 /0
And now I want some variables, so I can use them as integer: a=1, a=2, a=3, a=4
How can I do that?
The answers given so far are correct, as long as your string is formatted the way you expect. You should always check the return value of sscanf to make sure things worked okay. sscanf returns the number of conversions successfully performed, in the above case 4.
if (4 != sscanf(buf, "%d %d %d %d", &a, &b, &c, &d))
{
/* deal with error */
}
If buf was "1 2 3" or "1 2 a b" or something, sscanf would return a short item count.
As others have noted, if you know how many numbers to expect, sscanf is the easiest solution. Otherwise, the following sketches a more general solution:
First tokenize the string by spaces. The standard C method for this is strtok():
char* copy;
char* token;
copy = strdup(string); /* strtok modifies the string, so we need a copy */
token = strtok(copy, " ");
while(token!=NULL){
/* token now points to one number.
token = strtok(copy, " ");
}
Then convert the string to integers. atoi() will do that.
If the string always contains 4 numbers delimited with spaces, then it could be done with sscanf:
sscanf(string, "%d %d %d %d", &a, &b, &c, &d);
If the count of numbers varies, then you would need to parse the string.
Please clarify your question accordingly.
sscanf() can do that.
#include <stdio.h>
int main(void)
{
int a, b, c, d;
sscanf("1 2 3 4", "%d %d %d %d", &a, &b, &c, &d);
printf("%d,%d,%d,%d\n", a, b, c, d);
}

Resources