I am trying to use fscanf to take a text file and simply print it in the same format (eventually it will be used to populate an array of structures).
When I use fscanf it prints some values which are not part of the text and I'm not sure where they are coming from. I have put a small part of the input text file and the output below the code section.
The values I want to print across the page horizontally are all in the first column of the output, which makes me think that its something to do with how I have defined my fprint statement? Each of the correct values in the first column are followed by values which I don't know where have come from.
Any help would be greatly appreciated.
#include <stdlib.h>
int count_lines(char file[]) {
FILE* f = fopen(file, "r"); /* declaration of file pointer */
char x;
int c = 0; /* declaration of variable */
f = fopen(file, "r");
if (f == NULL) {
printf("Cannot open file for reading");
return -1;
}
while ((x = fgetc(f)) != EOF) {
if (x == '\n') {
c = c + 1;
}
}
if (fclose(f) != 0) {
printf("File could not be closed.\n");
return -1;
}
printf("Number of lines = %d\n", c);
return c;
}
struct votes {
char state[100]; /* state name */
long dempv; /* democrats popular votes */
long demev; /* democrats electoral votes */
long reppv; /* republicans popular votes */
long repev; /* republicans electoral votes */
};
void initialise_votes(char file[], struct votes* arr, int nlines) {
FILE* f = fopen(file, "r");
char temp1[20];
long temp2;
long temp3;
long temp4;
long temp5;
if (f == NULL) {
printf("Cannot open file for reading\n");
}
while (fscanf(f, "%s, %ld, %ld, %ld, %ld", temp1, &temp2, &temp3, &temp4, &temp5) != EOF) {
printf("%s\t%ld\t%ld\t%ld\t%ld\n", temp1, temp2, temp3, temp4, temp5);
}
if (fclose(f) != 0) {
printf("File could not be closed.\n");
}
}
int main(void) {
char s_in[] = "uselection2012.txt"; /* input data file */
int nlines;
struct votes* arr;
nlines = count_lines(s_in);
arr = (struct votes*)malloc(sizeof(struct votes) * nlines);
initialise_votes(s_in, arr, nlines);
return 0;
}
Input file:
Alabama 795696 0 1255925 9
Alaska 122640 0 164676 3
Arizona 1025232 0 1233654 11
Arkansas 394409 0 647744 6
California 7854285 55 4839958 0
Output:
Alabama 6356696 -37862896 6380 0
795696 6356696 -37862896 6380 0
0 6356696 -37862896 6380 0
1255925 6356696 -37862896 6380 0
9 6356696 -37862896 6380 0
Alaska 6356696 -37862896 6380 0
122640 6356696 -37862896 6380 0
0 6356696 -37862896 6380 0
164676 6356696 -37862896 6380 0
3 6356696 -37862896 6380 0
Arizona 6356696 -37862896 6380 0
1025232 6356696 -37862896 6380 0
0 6356696 -37862896 6380 0
1233654 6356696 -37862896 6380 0
11 6356696 -37862896 6380 0
Arkansas 6356696 -37862896 6380 0
394409 6356696 -37862896 6380 0
0 6356696 -37862896 6380 0
647744 6356696 -37862896 6380 0
6 6356696 -37862896 6380 0
California 6356696 -37862896 6380 0
7854285 6356696 -37862896 6380 0
55 6356696 -37862896 6380 0
4839958 6356696 -37862896 6380 0
0 6356696 -37862896 6380 0
Your scanf format string includes commas, but your input data does not.
Note that fscanf returns either EOF, or the number of values successfully scanned. You can and should use that return value to check for errors, and doing so would have pointed you to the problem in your code.
The commas in your fscanf format string tell fscanf to expect commas in the file and to stop if it does not find them.
There are no commas in your file, so fscanf stops after reading a “string” for the %s conversion.
Remove the commas from the format string.
Test that the return value of fscanf equals the number of items you expect to be assigned, not just that it is not equal to EOF.
Avoidable coding weakness lead to OP's difficulty
Had code checked the return value against the desired result of 5 rather than one of the many incorrect ones like EOF, 0, 1, 2, 3, 4, the issue would have been quickly narrowed to a scanf failure.
// while (fscanf(f, "%s, %ld, %ld, %ld, %ld", temp1, &temp2, &temp3, &temp4, &temp5) != EOF) {
while (fscanf(f, "%s, %ld, %ld, %ld, %ld", temp1, &temp2, &temp3, &temp4, &temp5) == 5) {
Other issues
White-space
No value, other than maybe style, to put a " " before "%ld" as "%ld" already consumes optional leading white-space.
Yet there is value to put a space before the "," to allow for optional leading white-space input before the ','.
while (fscanf(f, "%s ,%ld ,%ld ,%ld ,%ld", temp1, &temp2, &temp3, &temp4, &temp5) == 5) {
Buffer overflow
Never use "%s" in a (f)scanf() function. Use a width limit, else risk buffer overflow.
// width --------vv
while (fscanf(f, "%19s ,%ld ,%ld ,%ld ,%ld", temp1, &temp2, &temp3, &temp4, &temp5) == 5) {
fgetc() returns an int
fgetc(f) returns 257 different values. Use an int to correctly distinguish.
// char x;
int x;
...
while ((x = fgetc(f)) != EOF) {
Line count may fail
Count of lines only counted the number of '\n'. Had the file only contained "abc 1 2 3 4", with no '\n', the line count would report as 0.
Instead count the number of line beginnings.
count = 0;
int prior = '\n';
while ((x = fgetc(f)) != EOF) {
if (prior == '\n') {
count++;
}
prior = x;
...
}
Related
I'm working on a program which reads every integer in csv file and copies it into a buffer so that I can later use it to construct a binary search tree with it. I'll show my code, then I'll explain the issue I'm having:
Code -
int *createBuffer(int count) {
FILE *file = fopen(FILE1, "r");
int buffer[count + 1];
int *bufferPointer = buffer;
int number;
int ch;
int i = 0;
while (1) {
ch = fgetc(file);
if(ch == EOF){
break;
}
if (fscanf(file, "%i", &number)) {
buffer[i] = number;
i++;
}
}
return bufferPointer;
}
Count refers to the number of commas that are present in the file so I can allocate enough space for each number in the array. The file pointer points to the file I'm opening in read-only mode. The buffer is created using the aforementioned count variable. bufferPointer is the pointer to the buffer that I'm returning from the function. The while loop runs until the variable ch is equal to EOF at which point it breaks. The if statement's purpose is basically to scan the file for integers and read them into number, and then copy number into the next buffer index. Finally, the buffer pointer is returned.
This code is giving me extremely strange results. When I print the buffer, I get the result:
9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 850045856 0 -2141008008 32767 0 0 214814639 1 0 0 -2141007448 32767 0 0 214814639 1 -487430544 32766 539243238 32767 -2141007448 32767 6 0 -487430496 32766 539279361 32767 0 0 0 0 0 0 0 0 -487430272 32766 539271526 32767 92 68 68 0 0 0 69 0 -2141007976 32767 0 0 42 68 55 46 10 40 44 100 75 63 19 13 10 95 43 47 47 49 59 40 0 0 -2141006600 %
The reason this is strange is because although I am getting some garbage values, the entire sequence from 42...40 matches numbers in my data file. I'm not exactly sure where I'm going wrong in this code so if anyone knows, please do share.
As always, if you take the time to answer or attempt to answer this question, thank you for your time. If you need further clarification, don't hesitate to ask.
This is a "fixed" version of your code. But you will notice that it does not print the first character. Lets say, if the first number in your file is, 220, then it will print 20.
The reason is - your program first takes away a character from file in c=fgetc(file). So at first iteration, it takes away the first character 2 from 220 and then stores 20 in the memory. Thought this problem does not occur for the rest of the iterations because the first character is comma in those cases.
To go around that problem, we can just put c=getc(file) at the end of the loop. This way, after entering the loop, it reads the first number, gets rid of the comma, reads next number, gets rid of the comma....
#include<stdio.h>
#include<stdlib.h>
int *createBuffer(int count) {
FILE *file = fopen("filename.txt", "r");
int* buffer = (int*)malloc(sizeof(int)*(count + 1));
int number;
int ch;
int i = 0;
while (1) {
if (fscanf(file, "%i", &number)) {
buffer[i] = number;
i++;
}
ch = fgetc(file);
if(ch == EOF){
break;
}
}
return buffer;
}
void main(){
int* arr = createBuffer(10);
for(int i=0; i<10; i++){
printf("%d ",arr[i]);
}
printf("\n");
}
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?
I'm trying to read a .txt file, which contains a name and a last name in the first line and below contains an array
The problem is that the first line may or may not one letter A in brackets like this: [A].
for instance:
Jose Perez [A] or may have jose perez
When I run here mentioned code, if the first line does not contain a [A] performs a shift of values, for example the value of the matrix [1] [1] is up as tester and insert a 0 at the end to complete the matrix.
Here is a sample of what gives the code when there is a [A] in the first line and when not
FILE* text=NULL;
text=fopen(archivo,"r");
char name[100];
char last_name [100];
char verifier [10];
int matriz[6][4];
int i ;
int lu,ma,mi,ju,vi;
if (text == NULL) {
}
else {
fscanf(text,"%s %s %s [^\n]",name, last_name, verifier);
for( i= 0; i<7;i++){
fscanf(text,"%d %d %d %d %d [^\n]",&lu, &ma, &mi,&ju,&vi);
matriz[i][0] = lu;
matriz[i][1] = ma;
matriz[i][2] = mi;
matriz[i][3] = ju;
matriz[i][4] = vi;
}
Result:
Jose Perez 1
0 0 0 0 0
1 0 1 0 1
0 1 1 1 1
0 1 1 0 0
1 0 0 0 0
1 1 1 0 0
1 0 1 0 0
Juan Perez A
1 0 1 1 1
0 0 1 1 1
0 0 1 0 1
0 0 1 1 1
1 0 1 0 1
0 0 1 1 1
0 1 1 1 0
As could be solved?
char line[80], fname[16], lname[16], third[4];
fgets(line, 80, fp)
if (sscanf(line, "%s %s %s\n",
fname, lname, third) == 3 && strcmp(third, "[A]") == 0)
// the line has a [A] at the end
else if (sscanf(line, "%s %s\n", fname, lname) == 2)
// the line does not have a [A] at the end
else
fprintf(stderr, "Invalid line");
fgets reads the next line from the file. If it is not null, we check the return value of sscanf. It returns the number of items parsed. So, on success it should return 3. If it contains a [A] at the end of the line, the third variable should compare equal to [A].
After all this, we can proceed to read the array.
Since your verifier is always a single character, you can let scanf do the work. In the example below it will stop scanning if the opening bracket does not exist, but get the verifier character otherwise:
char name[100];
char last_name [100];
char verifier = 0;
int n = fscanf("%s %s [%c]", name, last_name, &verifier);
If n == 2 there was no verifier, if n == 3 there was a verifier, and in any other case, there was an error.
I'm reading in from a file emp and it was reading when the file (which is the last file below -- which worked) was structured as 10 records with a header (being skipped by the fseek() of 0 in the code). But when it reads in with the 3x10 format (which is the middle file below, which failed to read properly) with headers for each block of 3 -- that's failing. I'm not sure why the conditional just within the loop isn't catching and the 2nd conditional within the loop isn't printing everything marked with a 0 upfront.
int nDeleteSwitch;
char sSSN[10], sName[21];
float nSalary;
char nextIn[3];
printf("SSN NAME SALARY\n");
mioHashFile = fopen("emp", "r");
fscanf (mioHashFile,"%d",&mnOverFlowRecords);
fseek (mioHashFile, mnHeaderSize, 0); //Skip past the CRLF at end of overflow counter
//int numHeadRec = mnOverFlowRecords/3;
/* sequentially print all active records */
//for(int i=0;i<(numHeadRec+mnOverFlowRecords);i++)
for(int i=0;i<20+mnOverFlowRecords;i++)
{
if ((fscanf(mioHashFile,"%d",nextIn)== -1) || (fscanf(mioHashFile,"%d",nextIn)== 0) ||
(fscanf(mioHashFile,"%d",nextIn)== 1))
{
fscanf(mioHashFile,"%d%s%s%f",&nDeleteSwitch,sSSN,sName,&nSalary);
//printf("%d",nDeleteSwitch);
if (nDeleteSwitch==0)printf("%-11s%-21s%-10.2f\n",sSSN,sName,nSalary); // wtf why this isn't printing
else if (nDeleteSwitch == -1) printf("there's a -1 on row: %d",i);
}
else {continue;};
}
fclose(mioHashFile);
printf("Print Table Complete\n");
And here we have emp file that it's refusing to read the 0 entries from:
0
Overflow page: 0 0 -1
-1 x x 0.00
-1 x x 0.00
0 x x 0.00
Overflow page: 1 0 -1
-1 x x 0.00
-1 x x 0.00
0 x x 0.00
Overflow page: 2 0 -1
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
Overflow page: 3 0 -1
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
Overflow page: 4 0 -1
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
Overflow page: 5 0 -1
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
Overflow page: 6 0 -1
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
Overflow page: 7 0 -1
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
Overflow page: 8 0 -1
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
Overflow page: 9 0 -1
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
So it won't read that, but it'll read this:
0
0 123 asd 789.00
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
0 345 zxc 234.00
-1 x x 0.00
-1 x x 0.00
-1 x x 0.00
There's a space right before the -1 and there'd be 2 spaces before the 0. As shown in the code, I'm trying to print anything with a 0 at the beginning, and skip over the 'header' lines at the front of each hash block. When I try to force it to print (like print < 3) then it just comes out as garbage symbols.
What it should be doing is printing all records that have a 0 at the beginning and skipping over the headers (because they don't have -1,0,1 at the beginning).
Given the declaration:
char nextIn[3];
This code is wrong:
if ((fscanf(mioHashFile,"%d",nextIn) == -1) || (fscanf(mioHashFile,"%d",nextIn) == 0) ||
(fscanf(mioHashFile,"%d",nextIn) == 1))
{
You are passing an array of three characters and expecting fscanf() to treat it as an int. Your conditions are odd, too. EOF is not guaranteed to be -1 (though I don't recall a system where it was not). There wouldn't be much point in trying again after detecting EOF (not without other activity to clear the EOF marker).
When I try this fscanf(mioHashFile,"%s",nextIn); if (strcmp(nextIn, '-1') == 0) it still crashes and burns opulently due to type incompatibility. #Emmet also caught my error on the EOF (in that answer's code). How would you do it, and still maintain the scanf(), printf() formatting that I'm trying to use?
That requires some semi-real coding. I'd use fgets() and sscanf() rather than fscanf():
char line[4096];
int lineno = 0;
while (fgets(line, sizeof(line), mioHashFile) != 0)
{
if (++lineno == 1)
continue; // Skip the offset line
if (line[0] == 'O') // Overflow line - skip
continue;
if (sscanf(line, "%d %9s %20s %f", &nDeleteSwitch, sSSN, sName, &nSalary) != 4)
{
fprintf(stderr, "Failed to scan data from: %s", line);
continue;
}
if (nDeleteSwitch == 0)
printf("%-11s%-21s%-10.2f\n", sSSN, sName, nSalary);
else if (nDeleteSwitch == -1)
printf("there's a -1 on row: %d\n", lineno);
else
printf("The delete switch value is %d\n", nDeleteSwitch);
}
Note that %s skips leading white space and then stops scanning at the next white space.
Almost all of the detail you supply seems irrelevant to your stated objective of printing out lines that begin with a zero and not printing out the others. The following program, for example, prints out all of the lines that begin with a zero, but no others.
#include <stdio.h>
#include <stdlib.h>
#define BUF_SZ (512)
#define INFILE "datafile.txt"
int main(void) {
char buf[BUF_SZ] = {0};
FILE *fp;
long first;
char *endp;
/* Open input file successfully or bail */
fp = fopen(INFILE, "r");
if( fp == NULL ) {
fprintf(stderr, "Failed to open '%s'\n", INFILE);
exit(EXIT_FAILURE);
}
/* Read each line, convert beginning to long and print if it's 0 */
while( NULL!=fgets(buf, BUF_SZ, fp) && !feof(fp) ) {
first = strtol(buf, &endp, 10);
if( first==0L && endp!=buf ) {
fputs(buf, stdout);
}
}
fclose(fp);
return 0;
}
I'm writing a simple program in C to calculate grades and grade averages. I feed it a textfile from the command line via the "< xyz.txt" and output to another text via "> xyz_output.txt" commands. There are over 100 lines of data in the input file, but the program keeps exiting the loop around line 6 of the input for some reason... I've checked the input format several times and I can't seem to figure out why it's exiting prematurely. Here is the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char pp[4];
char school[3];
int class;
int student;
int a;
int b;
int c;
int d;
int e;
int f;
int g;
int h;
int i;
int j;
int k;
int l;
int m;
int n;
int o;
int p;
int q;
int r;
float average;
int runtotal;
int grouptotal = 0;
float groupaverage;
int counter = 0;
while (scanf("%3s%2s%2i%2i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", pp, school, &class, &student, &a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n, &o, &p, &q, &r) == 22)
{
runtotal = (a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r);
average = (float)runtotal / 18;
grouptotal = grouptotal + runtotal;
counter++;
printf ("%i.\n", counter);
printf ("%s%s-%i-%i -- %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i %1i\n", pp, school, class, student, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r);
printf ("Total score: %i\n", runtotal);
printf ("Average: %f\n", average);
printf ("Running Total: %i\n\n", grouptotal);
}
groupaverage = (float)grouptotal / counter;
printf ("Calculations complete.\nThe total score for this group was %i.\nThe average score for this group was %f.\n", grouptotal, groupaverage);
return 0;
}
and here are the first 10 lines of the actual input .txt file.
preKO6101 0 0 1 1 1 1 1 0 1 0 1 0 0 1 1 0 2 0
preHI6114 1 0 0 1 0 1 1 1 1 0 0 0 0 1 1 1 2 1
preHI5116 0 0 0 1 0 0 1 1 1 0 0 0 0 2 0 0 1 0
preHI6103 0 0 0 1 0 0 1 1 1 0 0 0 0 1 1 0 0 1
preHI5132 0 0 0 1 1 0 1 1 1 0 0 0 0 1 0 0 0 0
preHI5109 1 0 0 1 1 0 0 1 0 0 0 0 1 1 1 2 2 1
preHI6113 2 1 1 1 1 0 1 0 0 0 1 0 1 1 1 0 0 1
preSA5116 0 1 0 1 1 2 1 2 0 0 0 0 2 1 2 0 2 2
preHI6109 0 0 0 1 1 0 1 1 1 0 1 1 0 1 1 0 1 1
preHI5107 0 0 0 1 1 0 1 1 1 0 0 0 0 1 1 0 1 0
It seems to make it to 'preHI5109' but then exits. I have a feeling that it has to do with how I'm using scanf, but I'm not sure what I'm doing wrong. Any advice on how to fix this?
The method employed to read the data file is very sensitive to mis-alignment, parsing and I/O issues. Suggest more robust IO and parsing as certainly some small error exist in the data file. Use fgets() to control reading the line orientated data.
char buf[100];
while (fgets(buf, sizeof buf, stdin) != NULL) {
int count = sscanf(buf, "%3s%2s%2i%2i %i %i %i %i /* ... */ %i %i",
pp, school, &class, &student, &a, &b, &c, /* ... */ &q, &r);
if (count != 22) {
printf("Error %d (TBD relevant text)\n", count);
break;
}
/* continue with normal processing */
}
Other improvements
int a[18] instead of int a; int b; ...
Use loops to then scan, sum, print the 18 elements.
Use "%i" rather than "%1i". The 1 is the minimum width, so why have it?
Note: If the input file was supplied by a Prof., It would not surprise me that something intentionally bad is in it. The lesson: do not trust data input unless well vetted. User input is evil.