fgets and strtok in a double "for" loop - c

I'm trying to extract the content of a file into a matrix but the file may look completely differently.
For exemple all these files should give the same result : a 3x3 matrix containing 1,2,3,4,5,6,7,8,9.
1 2 3
4 5 6
7 8 9
1 2 3
4 5 6
7 8 9
1 2 3 4
5
6
7 8
9
1 2 3
$something
$something else
4 5 6
$something else else
7 8 9
Hopefully I know the dimensions of the matrix beforehand as well as the "$" character that indicates that these lines are to be ignored in the current process.
My current algorithm using fscanf works great but It can't work with the "$something" lines.
I figured that I should use the fgets/strtok/sscanf method but there are some issues.
// File* file (already assigned)
char line[32]; //assuming 32 is enough
char* token;
fgets(line,32,file);
token = strtok(line," \t");
for (y=0; y<ySize; y++)
{
for (x=0; x<xSize, x++)
{
if (token[0] == '$') //should use a str function
{
fgets(line,32,file);
token = strtok(line," \t")
x--;
}
else
{
if (we are at the end of the line)
{
fgets(line,32,file);
token = strtok(line," \t")
}
sscanf(token,"%d",&matrix[x][y];
token = strtok(NULL," \t");
}
}
}
Basically I'd like to have some help to write the "if (we are at the end of the line)" condition and some input on my method, is it flawless? Did I correctly thought of the process?
Thank you.

You should use getline instead of fgets to make things easier. The latter is unreliable. The test condition that you are looking for is:
token == NULL;
Check this: "Once the terminating null character of str has been found in a call to strtok, all subsequent calls to this function with a null pointer as the first argument return a null pointer."

You can easily parse without strtok() by using strspn() / strcspn(), and sscanf()s "%n" specifier. Also: there was a ',' in the original code where a ';' should have been.
#include <stdio.h>
#include <string.h>
#define XSIZE 3
#define YSIZE 3
int matrix[XSIZE][YSIZE];
int main(void)
{
char line[200];
int pos,eat,xx,yy, rc;
xx = yy =0;
while ( fgets(line, sizeof line, stdin) ) {
if (line[0] == '$') continue;
for(pos=0; line[pos]; pos += eat) {
pos += strspn(line+pos, " \t\n");
rc =sscanf(line+pos, "%d%n", &matrix[xx][yy], &eat);
if (rc < 1) break;
if (++xx >= XSIZE ) {xx = 0; if(++yy >= YSIZE) goto done; }
}
}
done:
/* show it to the world ... */
for (yy = 0; yy < YSIZE; yy++) {
for (xx = 0; xx < XSIZE; xx++) {
fprintf (stdout, " %d", matrix[xx][yy] );
}
fprintf (stdout, "\n" );
}
return 0;
}

Related

How can print numbers is separate lines?

The question to solve is that this code should get numbers in separate lines until 0 is given. Then it should print y number, y times. For example if number 3 is given, it should print 3, 3 times in separate lines.
I try to get the inputs from the user in separate lines. I mean one input in one line. Then print the numbers in separate lines. I don't know where to add \n to solve it.
This is my code:
#include <stdio.h>
int main() {
int y = 1;
while (y != 0) {
scanf("%d", &y);
if (y == (0)) {
break;
}
for (int i = 1; i <= y; i++) {
printf("%d\n", y);
}
}
}
I tried to add \n beside %d of scanf but it didn't work as I expected.
The output of this code is like this:
1
1
2
2
2
3
3
3
3
4
4
4
4
4
0
What I expect is that all the inputs should be given in separate lines before output is printed.
input like this:
1
2
3
4
0
output like this:
1
2
2
3
3
3
4
4
4
4
As #DavidC.Rankin explains below, if you want to retain the input it means you have to store it somewhere. It's usually in memory and either the original input string (char []) or you store the data in a format suitable for output like int input[LEN] that I use below.
Other options includes using a recursive function to store one number on the stack, or a file or an external service like a database (possible hosted in the cloud these days).
#include <stdio.h>
#define LEN 5
int main(void) {
// input
int input[LEN];
int i = 0;
for(; i < LEN; i++) {
if(scanf("%d",input + i) != 1) {
printf("scanf failed\n");
return 1;
}
if(!input[i])
break;
}
// output (copy of input other than 0)
for(int j = 0; j < i; j++) {
printf("%d\n", input[j]);
}
// output (repeated based on input)
for(int j = 0; j < i; j++) {
for(int k = 0; k < input[j]; k++) {
printf("%d\n", input[j]);
}
}
}
and example run:
1 # input
2
3
4
0
1 # output (copy of input other than 0)
2
3
4
1 # output (repeated based on input)
2
2
3
3
3
4
4
4
4
Here is the as close you can get with a recursive function and no arrays:
#include <stdio.h>
#include <stdlib.h>
void read_then_print() {
// input
int d;
if(scanf("%d", &d) != 1) {
printf("scanf failed\n");
exit(1);
}
if(d) {
// output before recursion
printf("%d\n", d);
read_then_print();
}
// output after recursion
for(int i = 0; i < d; i++) {
printf("%d\n", d);
}
}
int main(void) {
read_then_print();
}
and example session:
1 # input
1 # output before recursion
2 # input
2 # output before recursion
3 # ...
3
4
4
0
4 # output after recursion
4
4
4
3
3
3
2
2
1
#include<stdio.h>
int main()
{
int y;
do
{
scanf("%d",&y);
for(int i = 1;i <= y;i++)
{
printf("%d\n", y);
}
}while(y != 0);
}
Use this one this might solve your problem.
You want to gather all the input before processing the lines and printing the results. You can use an array to store the input numbers and process them once you read a 0.
Here is a modified version with this approach:
#include <stdio.h>
#define MAX_LINES 256
int main() {
int input[MAX_LINES];
int y, n = 0;
while (n < MAX_LINES && scanf("%d", &y) == 1 && y != 0) {
input[n++] = y;
}
for (int i = 0; i < n; i++) {
y = input[i];
for (int i = 0; i < y; i++) {
printf("%d\n", y);
}
}
return 0;
}

Character comparison in C fails

I'm trying to parse a Fen String (char *starting_fen), but it doesn't seem to be working.
starting_fen: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
void init_board(char *starting_fen)
{
int rank = 7, file = 0, fen_length = strlen(starting_fen);
for (int i = 0; i < fen_length; i++)
{
if (starting_fen[i], "/") // <-- works for all characters?
{
printf("SYMBOL # idx %d ==> '%c'\n", i, starting_fen[i]);
// file = 0;
// rank--;
}
}
}
This is my output:
SYMBOL # idx 0 ==> 'r'
SYMBOL # idx 1 ==> 'n'
SYMBOL # idx 2 ==> 'b'
//...
SYMBOL # idx 53 ==> '0'
SYMBOL # idx 54 ==> ' '
SYMBOL # idx 55 ==> '1'
I tried using strcmp (if (strcmp(starting_fen[i], "/"))) but got a Segmentation fault...
I answer this because it is a funny bug. Here:
if (starting_fen[i], "/")
You are calling , operator. So it returns "/" which always will be evaluated to true (Since it is a address which will have a value not equal to 0). Your code need to be like this:
if (starting_fen[i] == '/')
So 2 changes:
You need == operator to check equality.
Characters are identified by ' not ".
This fails:
if (starting_fen[i], "/") {
because starting_fen[i], "/" is always "/" which is not null. See the wikipdia page about the comma operator
This fails:
if (strcmp(starting_fen[i], "/")) {
Because starting_fen[i] will be a char, not a pointer to a string. Thus it will try to read a location in early memory and fail.
This fails:
if (starting_fen[i] == "/") {
Since you are trying to compare a char to a pointer.
This is correct:
if (starting_fen[i] == '/') {
You can save yourself a lot of grief if you use proven standard library functions.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *starting_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
printf( "Given: '%s'\n", starting_fen );
char *rank[8];
char *cp = starting_fen;
for( int i = 0; i < sizeof rank/sizeof rank[0]; i++ ) {
rank[ i ] = cp;
cp = strpbrk( cp, "/ " ); // standard library function
*cp++ = '\0';
printf( "%d '%s'\n", i, rank[ i ] );
}
printf( "The rest: '%s'\n", cp );
return 0;
}
Output:
Given: 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
0 'rnbqkbnr'
1 'pppppppp'
2 '8'
3 '8'
4 '8'
5 '8'
6 'PPPPPPPP'
7 'RNBQKBNR'
The rest: 'w KQkq - 0 1'
You may want to start the array index with 7 and count down to 0. That's for you to decide.

for loop is only printing last iteration

I am trying to write a C program that takes a string input from a user and then looks into the input to count the frequency of all the integers in the string. So suppose if the user gives the input:
a11472o5t6
the output would be :
0 2 1 0 1 1 1 1 0 0
my approach involves comparing every character of the string to all 10 digits one by one and if any the character is equal to the digit, it would increment the isdigit number by 1.
the code I wrote for the same is as follows:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int is1 = 0; //initialise integers for checking
int is2 = 0;
int is3 = 0;
int is4 = 0;
int is5 = 0;
int is6 = 0;
int is7 = 0;
int is8 = 0;
int is9 = 0;
int is0 = 0;
int main()
{
char s[100]; //initialise array for string
scanf("%s", s); //scan string input
//now all characters of the string are stored in the array s
for (int i = 0; i < strlen(s); i++) //loop to iterate over all the elements in the array
{
if (strcmp(&s[i], "0") == 0)
{
is0 = is0 + 1;
}
if (strcmp(&s[i], "1") == 0)
{
is1 = is1 + 1;
}
if (strcmp(&s[i], "2") == 0)
{
is2 = is2 + 1;
}
if (strcmp(&s[i], "3") == 0)
{
is3 = is3 + 1;
}
if (strcmp(&s[i], "4") == 0)
{
is4 = is4 + 1;
//printf("%d", is4);
}
if (strcmp(&s[i], "5") == 0)
{
is5 = is5 + 1;
}
if (strcmp(&s[i], "6") == 0)
{
is6 = is6 + 1;
}
if (strcmp(&s[i], "7") == 0)
{
is7 = is7 + 1;
}
if (strcmp(&s[i], "8") == 0)
{
is8 = is8 + 1;
}
if (strcmp(&s[i], "9") == 0)
{
is9 = is9 + 1;
}
}
printf("%d ", is0);
printf("%d ", is1);
printf("%d ", is2);
printf("%d ", is3);
printf("%d ", is4);
printf("%d ", is5);
printf("%d ", is6);
printf("%d ", is7);
printf("%d ", is8);
printf("%d ", is9);
}
I expected the code to iterate over and over for the entire length of the string and and update values of the isdigit series every time a number was successfully found. However whenever I run the code only the last digit seems to find its place in the output .
for example if I type 54 as an input the expected output is
0 0 0 0 1 1 0 0 0 0
however the output my code seems to be giving is
0 0 0 0 1 0 0 0 0 0
likewise the number 45 also has the same expected output
0 0 0 0 1 1 0 0 0 0
but the output I am receiving is
0 0 0 0 0 1 0 0 0 0
which looks like the code overwrites any operation that occurred in the previous iteration, but I can't seem to understand why and how to fix it.
On my part I checked if the characters were being called properly one by one and that all characters were being compared, where I found no problem. I also looked up other answers on stack overflow and elsewhere, but was I am a beginner and most answers were written in reference to languages that I can't understand, so I was unable to relate to the solution they were being told. the closest I got was someone who was using a single variable repetitively thus overwriting it in each iteration. however I have declared sufficient variables( one for each digit from 0-9), so that shouldn't be the problem in my code either.
while I know this question could be solved easily using arrays, I would like to know what I was doing wrong here to avoid any such mistakes in the future.
When you do if (strcmp(&s[i],"1")==0) you are comparing strings, not individual characters, which is why only the last character is counted. It's the only one matching.
Example:
If s == "a11472o5t6" and you use strcmp(&s[1], "1"), you would be comparing the string "11472o5t6" with the string "1", which clearly will not be equal.
You want if(s[i] == '1') etc. to do the comparisons of individual characters instead.
And you are correct about using arrays instead. It'd certainly be easier.
Example:
#include <ctype.h>
#include <stdio.h>
int main() {
const char *str = "a11472o5t6";
int ints[10] = {0};
for (const char *chptr = str; *chptr != '\0'; ++chptr) {
if(isdigit((unsigned char) *chptr)) ++ints[*chptr - '0'];
}
for (int i = 0; i < 10; ++i) printf("%d %d\n", i, ints[i]);
}
Output:
0 0
1 2
2 1
3 0
4 1
5 1
6 1
7 1
8 0
9 0

How read a variable number of int from a string

I have the following text file
0 0 0 debut
1 120 0 permis exploitation
2 180 1 1 piste 6km
3 3 1 2 installation sondeuses
4 30 1 2 batiments provisoires
5 60 1 2 groudronnage piste
6 90 1 4 adduction eau
7 240 2 3 4 campagne sondage
8 180 3 5 6 7 forage 3 puits
9 240 3 5 6 7 construction bureaux logements
10 30 2 8 9 transport installation matériel
11 360 2 8 9 traçage du fond
12 240 2 8 9 construction laverie
13 0 3 10 11 12 fin des travaux
Each line is the representation of a task and is described as followed: the first number is and ID, the second is the duration, the third is the number of previous tasks that are required, and all the numbers afterward are the IDs of the required tasks. Finaly the string in the end is the title of the string.
I'm trying to fill an array of those struct by reading this file. Here is the struct:
typedef struct{
int id;
int duration;
int nbPrev; /* number of required previous tasks */
int prev[NMAXPREV]; /*array of required previous tasks*/
char title[LGMAX];
}Task ;
Here is my code to read the file
int readTasksFile(char* file_name, Task t[])
{
FILE* f;
char line[256] = {'\0'};
int i = 0;
char c[1] = {0};
if((f = fopen(file_name, "r")) == NULL)
{
perror("The file couldn't be opened");
exit(EXIT_FAILURE);
}
while (fgets(line, 256, f) != EOF)
{
sscanf_s(line, "&d &d &d", &(t[i].id), &(t[i].duration), &(t[i].nbPrev));
i++;
}
fclose(f);
return 0;
}
How can I read all the previous tasks number in a line considering it is variable and still be able to read the title afterward ?
How can I read all the previous tasks number in a line considering it is variable and still be able to read the title afterward ?
The 3rd int should be the number of following ints.
Use "%n" to record scan offset.
After reading the .prev[], copy the rest of the line to .title.
Add error checking. This is very important, especially for complex input.
// Untested code to get OP started
// while (fgets(line, 256, f) != EOF) Comparing against EOF is incorrect
while (fgets(line, sizeof line, f)) {
int offset = 0;
// Use %d, not &d
if (sscanf(line, "%d %d %d %n",
&t[i].id, &t[i].duration, &t[i].nbPrev, &offset) != 3) {
// Handle bad input, for now, exit loop
break;
}
if (t[i].nbPrev < 0 || t[i].nbPrev > NMAXPREV) {
// Handle bad input, for now, exit loop
break;
}
char *p = line + offset;
int prev;
// Populate t[i].prev[]
for (prev = 0; prev < t[i].nbPrev; prev++) {
if (sscanf(p, "%d %n", &t[i].prev[prev], &offset) != 1) {
break;
}
p += offset;
}
if (prev != t[i].nbPrev) {
// Handle bad input, for now, exit loop
break;
}
// remaining text
int len = strlen(p);
if (len > 0 && p[len-1] == '\n') p[--len] = '\0'; // consume potential trailing \n
if (len >= LGMAX) {
// Handle bad input, for now, exit loop
break;
}
strcpy(t[i].title, p);
i++;
}
return i; // Let caller know of successful lines parsed.
Advanced: robust code would use strtol() instead of "%d" and sscanf().
readTasksFile() should also pass in the max number of Task t[] that can be read.
You could also scan by line and assign the two first numbers to id and duration, then do an int analysis and add the rest of the elements to nbPrev until you encounter a letter.
I don't know if this would be the best way to do it, but it's how I would do it.
Why don't you create also a list each time you register in struct nbPrev?
Like, instead of nbPrev being of type int, make it of type list?

How can I stop before reading the next line?

I have to read a file like this:
0 -> 1:50 2:30 3:10
1 ->
2 -> 0:10 3:20
3 -> 1:20 2:10 3:30
Here's my code:
graphs = fopen(argv[2],"r");
if(graphs==NULL){
printf("File hostgraphs not found\n");
exit(EXIT_FAILURE);
}
while((err=fscanf(graphs,"%d ->",&num))==1){
row=num;
while((err1=fscanf(graphs," %d:%d ",&column,&visits))==2){
hostgraphs[row*n+column]=visits;
}
if(err1!=2)
break;
if(err==0){
printf("Hostgraph out of bounds\n");
exit(EXIT_FAILURE);
}
}
for(i=0;i<n;i++){
for(j=0;j<n;j++){
printf("%d ", hostgraphs[i*n+j]);
}
printf("\n");
}
It gives me the following result:
0 50 30 10
0 0 0 0
0 0 0 0
0 0 0 0
The expected output must be:
0 50 30 10
0 0 0 0
10 0 0 20
0 20 10 30
Can anyone help me please?
You can do this by reading the file, one line at a time, using getline, and then applying the same logic, as you are doing, on each line separately.
In the following code, I first get a single line from the file in buffer using getline method. Then I process that line as a FILE pointer fbuffer using fmemopen method, which is used to read a string like a file:
graphs = fopen(argv[2],"r");
if(graphs==NULL)
{
printf("File hostgraphs not found\n");
exit(EXIT_FAILURE);
}
char *buffer = NULL;
size_t len = 0;
while(getline(&buffer, &len, graphs) != -1)
{
FILE * fbuffer = fmemopen(buffer, len, "r");
while((err=fscanf(fbuffer,"%d -> ",&num))==1)
{
row=num;
while((err1=fscanf(fbuffer," %d:%d ",&column,&visits))==2)
{
hostgraphs[row*n+column]=visits;
}
if(err1!=2)
{
break;
}
if(err==0)
{
printf("Hostgraph out of bounds\n");
exit(EXIT_FAILURE);
}
}
free(buffer);
buffer = NULL;
len = 0;
}
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
printf("%d ", hostgraphs[i*n+j]);
}
printf("\n");
}
Note: I have just added code to make your existing code work, without making any changes to your code.
The fix is simple: remove the trailing space in the fscanf format string in
fscanf(graphs," %d:%d ",&column,&visits)
It should be simplified as
fscanf(graphs,"%d:%d", &column, &visits)
fscanf ignores the newline then after read first row, it moves to next row and read 5 and results 1 in err1. That is the root-cause. You can add fseek to move back and start new sequence as bellow code-snip or simply change the algorithm to another effective one.
graphs = fopen(argv[2],"r");
if (!graphs) {
printf("File hostgraphs not found<%s>\n", argv[1]);
exit(EXIT_FAILURE);
}
while((err = fscanf(graphs, "%d ->", &num)) == 1) {
pos = ftell(graphs); // save position
while((err1 = fscanf(graphs, " %d:%d", &column, &visits)) == 2 ) {
hostgraphs[num * n + column] = visits;
pos = ftell(graphs); // save position
}
// seek back to previous one and start new sequence
fseek ( graphs , pos, SEEK_SET );
}
for(i = 0; i< n; i++){
for(j = 0; j < n; j++){
printf("%d ", hostgraphs[i * n + j]);
}
printf("\n");
}
You were close, but you needed a way of locating each '\n' without reading the next row value from the file. That is very difficult to do with fscanf where you have varying number of elements in each row in your input file.
Another approach is to use fgets to read the entire line, and then separate the initial "row -> " prefix from the data values. If you move the values into a separate values buffer (say vbuf), you can then repeatedly loop through the buffer by finding the space, advancing to the next digit and then using sscanf to split the column and visits.
(you actually don't even need to split the values in vbuf, you can simply use a pointer to advance past the "row -> " text working with the entire line.)
The following example puts those pieces together and parses the values into the correct positions in hostgraphs. Look it over and let me know if you have questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { COLS = 4, MAXH = 16, MAXC = 64 };
int main (int argc, char **argv) {
char buf[MAXC] = "", vbuf[MAXC] = "";
char *p = NULL, *rfmt = "%d -> %[^\n]";
int row, column, visits, hostgraphs[MAXH] = {0};
size_t i, j, n = 0;
FILE *graphs = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!graphs) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, graphs))
{
*vbuf = 0; /* initialize vbuf, split %d:%d pars into vbuf */
if (sscanf (buf, rfmt, &row, vbuf) < 1)
break;
if (!*vbuf) { /* check vbuf contians values */
n++;
continue;
}
/* read 1st pair */
if (sscanf (vbuf, "%d:%d", &column, &visits) != 2) {
fprintf (stderr, "error: invalid line format\n");
exit (EXIT_FAILURE);
}
hostgraphs[row*COLS+column] = visits;
p = vbuf; /* assign p, parse remaining pairs */
while ((p = strchr (p, ' '))) /* find space */
{
while (*p < '0' || '9' < *p) /* find digit */
p++;
if (sscanf (p, "%d:%d", &column, &visits) == 2)
hostgraphs[row*COLS+column] = visits;
}
n++; /* advance row count */
}
for (i = 0; i < n; i++) { /* output values */
printf (" row[%2zu] : ", i);
for(j = 0; j < COLS; j++) {
printf (" %2d", hostgraphs[i * COLS + j]);
}
printf ("\n");
}
if (graphs != stdin)
fclose (graphs);
return 0;
}
Input File
$ cat ../dat/hostgraph.txt
0 -> 1:50 2:30 3:10
1 ->
2 -> 0:10 3:20
3 -> 1:20 2:10 3:30
Output
$ ./bin/hostgraph < ../dat/hostgraph.txt
row[ 0] : 0 50 30 10
row[ 1] : 0 0 0 0
row[ 2] : 10 0 0 20
row[ 3] : 0 20 10 30

Resources