My simple program is read from the text file one word at a time.
example text file. (below)
N 101001 Circular Queue 11 1
N 123456 List Linker 11 5
N 666666 Pseudocode Gen 38 3
N 110010 Stack Stretcher 3 2
and simple codes including variables etc...
char function;
int number, init_stock, reorder;
char info[100];
FILE *fp;
if((fp = fopen("input.txt", "r")) == NULL)
{
printf("input open failed\n");
return 0;
}
while(!feof(fp))
{
fscanf(fp, "%c %d %[A-z, ] %d %d ", &function, &number, info, &init_stock, &reorder);
}
When a variable was changed within the ' while loop',
I expected to affect it the next loop as well.
so.. first, 'Circular Queue' was stored in 'info'.
Second, when 'List Linker' was stored in 'info' because second string is shorter than first string,
I thought that the info array had string like this 'List Linkerue'.
info -- Circular Queue
info -- List Linker+ue
But the info seemed to be reset every time, and I do not know why.
On the first pass the content of info is "Circular Queue \0" The \0 at the end represents a single zero byte, it is the null character. This tells functions like printf where string ends.
Note that there is also a blank space at the end.
On the second pass the content of info is "List Linker \0e". As you suspected, fscanf didn't fill the whole string. But it did add the null character, so you can't see the e at the end. Content of info will be
1st pass: "Circular Queue " ... followed by random bytes
2nd pass: "List Linker \0e "
You can also improve your function by changing the format to %99[A-z, ] because info is 100 bytes (including the null character) And make sure fscanf reads 5 values as expected. You might also consider changing the input so the values are separated by commas.
while(fscanf(fp, "%c %d %99[A-z, ] %d %d ",
&function, &number, info, &init_stock, &reorder) == 5)
{
//optional: remove the extra space at the end of info
int len = strlen(info);
if(len)
if(info[len - 1] == ' ')
info[len - 1] = 0;
printf("string: [%s] %d\n", info, number);
}
Related
I am working on my school assignment and I've come across my first major hurdle... I am unable to write to a file or even retrieve input using scanf and fgets.
Problem 1:
FILE *f1;
char date_trans[100][15];
f1 = fopen("test.txt", "w");
if (f1 == NULL)
printf("File could not be opened.\n");
else
printf("Enter details (sender, receiver, amount.)\n");
for(i = 0; i <= element; i++)
{
for(j = 0; j <= 20; j++)
fgets(date_trans[i][j], 15, stdin);
printf("%s", date_trans[i][j]);
}
fclose(f1);
This is a small snippet; with the above code I am unable to retrieve the input entered and print it out on display/to a file.
My intent is to have 100 strings and up to 15 characters each.
So say if I were to enter a date like 18/12/15 or like 18-12-15, or a word like "Daniel", the program stops abruptly for a few seconds after I press enter and then proceeds to display "Press any key...".
I also receive this error...
[Warning] ...\assignment\test.c:22: warning: passing arg 1 of `fgets' makes pointer from integer without a cast
which I really do not understand as the variable date_trans is a string/an 2d array of characters, not an integer, right?
Edit:
Problem 2:
for (j=0; j <= 50 || !feof(f1); j++); // f1 opened in 'r' mode.
{
fscanf(f1, "%s %d %s %d %s %f", date_trans[j], &accsend[j], accnames[j],
&accreceive[j], accnamer[j], &amount_trans[j]);
printf("%d %s %d %s %d %s %.2f\n", j+1, date_trans[j], accsend[j], accnames[j],
accreceive[j], accnamer[j], amount_trans[j]);
}
In the code above, I am trying to input data through the stream f1 with function fscanf, which was opened in read mode. However the output makes the program hang and I am also unable to type or do any actions within the program.
As well, please advise whether my usage of !feof is correct/appropriate.
Thanks for any help.
I see two problems with the code you show:
The warning you get, because date_trans[i][j] is a char, not a char *
Your inner loop goes from 0 to 20 (inclusive) and you use that as index into an array with 15 elements (i.e. indexes from 0 to 14 inclusive).
To solve both problems, don't have the inner loop, instead in the outer do
fgets(data_trans[i], sizeof data_trans[i], stdin);
Problem number 1 also exists in the printf call, where you pass a single char to a format that expects a char *.
Lastly a note about your requirement of strings containing 15 characters: Don't forget the null-terminator, and its need for a space in the array as well. That means you currently have place for 14 characters plus the terminator.
Change this
fgets(date_trans[i][j], 15, stdin);
printf("%s", date_trans[i][j]);
to
fgets(date_trans[i], 15, stdin);
printf("%s", date_trans[i]);
date_trans[i][j] is char type not char *. Also you don't need j for loop.
I have a txt file that its lines are as follows
[7 chars string][whitespace][5 chars string][whitespace][integer]
I want to use fscanf() to read all these into memory, and I'm confused about what format should I use.
Here's an example of such line:
hello box 94324
Notice the filling whitespaces in each string, apart from the separating whitespace.
Edit: I know about the recommendation to use fgets() first, I cannot use it here.
Edit: here's my code
typedef struct Product {
char* id; //Product ID number. This is the key of the search tree.
char* productName; //Name of the product.
int currentQuantity; //How many items are there in stock, currently.
} Product;
int main()
{
FILE *initial_inventory_file = NULL;
Product product = { NULL, NULL, 0 };
//open file
initial_inventory_file = fopen(INITIAL_INVENTORY_FILE_NAME, "r");
product.id = malloc(sizeof(char) * 10); //- Product ID: 9 digits exactly. (10 for null character)
product.productName = malloc(sizeof(char) * 11); //- Product name: 10 chars exactly.
//go through each line in inital inventory
while (fscanf(initial_inventory_file, "%9c %10c %i", product.id, product.productName, &product.currentQuantity) != EOF)
{
printf("%9c %10c %i\n", product.id, product.productName, product.currentQuantity);
}
//cleanup...
...
}
Here's a file example: (it's actually 10 chars, 9 chars, and int)
022456789 box-large 1234
023356789 cart-small 1234
023456789 box 1234
985477321 dog food 2
987644421 cat food 5555
987654320 snaks 4444
987654321 crate 9999
987654322 pillows 44
Assuming your input file is well-formed, this is the most straightforward version:
char str1[8] = {0};
char str2[6] = {0};
int val;
...
int result = fscanf( input, "%7s %5s %d", str1, str2, &val );
If result is equal to 3, you successfully read all three inputs. If it's less than 3 but not EOF, then you had a matching failure on one or more of your inputs. If it's EOF, you've either hit the end of the file or there was an input error; use feof( input ) to test for EOF at that point.
If you can't guarantee your input file is well-formed (which most of us can't), you're better off reading in the entire line as text and parsing it yourself. You said you can't use fgets, but there's a way to do it with fscanf:
char buffer[128]; // or whatever size you think would be appropriate to read a line at a time
/**
* " %127[^\n]" tells scanf to skip over leading whitespace, then read
* up to 127 characters or until it sees a newline character, whichever
* comes first; the newline character is left in the input stream.
*/
if ( fscanf( input, " %127[^\n]", buffer ) == 1 )
{
// process buffer
}
You can then parse the input buffer using sscanf:
int result = sscanf( buffer, "%7s %5s %d", str1, str2, &val );
if ( result == 3 )
{
// process inputs
}
else
{
// handle input error
}
or by some other method.
EDIT
Edge cases to watch out for:
Missing one or more inputs per line
Malformed input (such as non-numeric text in the integer field)
More than one set of inputs per line
Strings that are longer than 7 or 5 characters
Value too large to store in an int
EDIT 2
The reason most of us don't recommend fscanf is because it sometimes makes error detection and recovery difficult. For example, suppose you have the input records
foo bar 123r4
blurga blah 5678
and you read it with fscanf( input, "%7s %5s %d", str1, str2, &val );. fscanf will read 123 and assign it to val, leaving r4 in the input stream. On the next call, r4 will get assigned to str1, blurga will get assigned to str2, and you'll get a matching failure on blah. Ideally you'd like to reject the whole first record, but by the time you know there's a problem it's too late.
If you read it as a string first, you can parse and check each field, and if any of them are bad, you can reject the whole thing.
Let's assume the input is
<LWS>* <first> <LWS>+ <second> <LWS>+ <integer>
where <LWS> is any whitespace character, including newlines; <first> has one to seven non-whitespace characters; <second> has one to five non-wihitespace characters; <integer> is an optionally signed integer (in hexadecimal if it begins with 0x or 0X, in octal if it begins with 0, or in decimal otherwise); * indicates zero or more of the preceding element; and + indicates one or more of the preceding element.
Let's say you have a structure,
struct record {
char first[8]; /* 7 characters + end-of-string '\0' */
char second[6]; /* 5 characters + end-of-string '\0' */
int number;
};
then you can read the next record from stream in into the structure pointed to by the caller using e.g.
#include <stdlib.h>
#include <stdio.h>
/* Read a record from stream 'in' into *'rec'.
Returns: 0 if success
-1 if invalid parameters
-2 if read error
-3 if non-conforming format
-4 if bug in function
+1 if end of stream (and no data read)
*/
int read_record(FILE *in, struct record *rec)
{
int rc;
/* Invalid parameters? */
if (!in || !rec)
return -1;
/* Try scanning the record. */
rc = fscanf(in, " %7s %5s %d", rec->first, rec->second, &(rec->number));
/* All three fields converted correctly? */
if (rc == 3)
return 0; /* Success! */
/* Only partially converted? */
if (rc > 0)
return -3;
/* Read error? */
if (ferror(in))
return -2;
/* End of input encountered? */
if (feof(in))
return +1;
/* Must be a bug somewhere above. */
return -4;
}
The conversion specifier %7s converts up to seven non-whitespace characters, and %5s up to five; the array (or char pointer) must have room for an additional end-of-string nul byte, '\0', which the scanf() family of functions add automatically.
If you do not specify the length limit, and use %s, the input can overrun the specified buffer. This is a common cause for the common buffer overflow bug.
The return value from the scanf() family of functions is the number of successful conversions (possibly 0), or EOF if an error occurs. Above, we need three conversions to fully scan a record. If we scan just 1 or 2, we have a partial record. Otherwise, we check if a stream error occurred, by checking ferror(). (Note that you want to check ferror() before feof(), because an error condition may also set feof().) If not, we check if the scanning function encountered end-of-stream before anything was converted, using feof().
If none of the above cases were met, then the scanning function returned zero or negative without neither ferror() or feof() returning true. Because the scanning pattern starts with (whitespace and) a conversion specifier, it should never return zero. The only nonpositive return value from the scanf() family of functions is EOF, which should cause feof() to return true. So, if none of the above cases were met, there must be a bug in the code, triggered by some odd corner case in the input.
A program that reads structures from some stream into a dynamically allocated buffer typically implements the following pseudocode:
Set ptr = NULL # Dynamically allocated array
Set num = 0 # Number of entries in array
Set max = 0 # Number of entries allocated for in array
Loop:
If (num >= max):
Calculate new max; num + 1 or larger
Reallocate ptr
If reallocation failed:
Report out of memory
Abort program
End if
End if
rc = read_record(stream, ptr + num)
If rc == 1:
Break out of loop
Else if rc != 0:
Report error (based on rc)
Abort program
End if
End Loop
The issue in your code using the "%9c ..."-format is that %9c does not write the string terminating character. So your string is probably filled with garbage and not terminated at all, which leads to undefined behaviour when printing it out using printf.
If you set the complete content of the strings to 0 before the first scan, it should work as intended. To achieve this, you can use calloc instead of malloc; this will initialise the memory with 0.
Note that the code also has to somehow consumes the newline character, which is solved by an additional fscanf(f,"%*c")-statement (the * indicates that the value is consumed, but not stored to a variable). Will work only if there are no other white spaces between the last digit and the newline character:
int main()
{
FILE *initial_inventory_file = NULL;
Product product = { NULL, NULL, 0 };
//open file
initial_inventory_file = fopen(INITIAL_INVENTORY_FILE_NAME, "r");
product.id = calloc(sizeof(char), 10); //- Product ID: 9 digits exactly. (10 for null character)
product.productName = calloc(sizeof(char), 11); //- Product name: 10 chars exactly.
//go through each line in inital inventory
while (fscanf(initial_inventory_file, "%9c %10c %i", product.id, product.productName, &product.currentQuantity) == 3)
{
printf("%9s %10s %i\n", product.id, product.productName, product.currentQuantity);
fscanf(initial_inventory_file,"%*c");
}
//cleanup...
}
Have you tried the format specifiers?
char seven[8] = {0};
char five[6] = {0};
int myInt = 0;
// loop here
fscanf(fp, "%s %s %d", seven, five, &myInt);
// save to structure / do whatever you want
If you're sure that the formatting and strings are the always fixed length, you could also iterate over input character by character (using something like fgetc() and manually process it. The example above could cause segmentation errors if the string in the file exceeds 5 or 7 characters.
EDIT Manual Scanning Loop:
char seven[8] = {0};
char five[6] = {0};
int myInt = 0;
// loop this part
for (int i = 0; i < 7; i++) {
seven[i] = fgetc(fp);
}
assert(fgetc(fp) == ' '); // consume space (could also use without assert)
for (int i = 0; i < 5; i++) {
five[i] = fgetc(fp);
}
assert(fgetc(fp) == ' '); // consume space (could also use without assert)
fscanf(fp, "%d", &myInt);
My problem is when I try to save the string (series[0]) Inside (c[0])
and I display it, it always ignore the last digit.
For Example the value of (series[0]) = "1-620"
So I save this value inside (c[0])
and ask the program to display (c[0]), it displays "1-62" and ignores the last digit which is "0". How can I solve this?
This is my code:
#include <stdio.h>
int main(void)
{
int price[20],i=0,comic,j=0;
char name,id,book[20],els[20],*series[20],*c[20];
FILE *rent= fopen("read.txt","r");
while(!feof(rent))
{
fscanf(rent,"%s%s%s%d",&book[i],&els[i],&series[i],&price[i]);
printf("1.%s %s %s %d",&book[i],&els[i],&series[i],price[i]);
i++;
}
c[0]=series[0];
printf("\n%s",&c[0]);
return 0;
}
The use of fscanf and printf is wrong :
fscanf(rent,"%s%s%s%d",&book[i],&els[i],&series[i],&price[i]);
Should be:
fscanf(rent,"%c%c%s%d",&book[i],&els[i],series[i],&price[i]);
You have used the reference operator on a char pointer when scanf expecting a char pointer, also you read a string to book and else instead of one character.
printf("1.%s %s %s %d",&book[i],&els[i],&series[i],price[i]);
Should be:
printf("1.%c %c %s %d",book[i],els[i],series[i],price[i]);
And:
printf("\n%s",&c[0]);
Should be:
printf("\n%s",c[0]);
c is an array of char * so c[i] can point to a string and that is what you want to send to printf function.
*Keep in mind that you have to allocate (using malloc) a place in memory for all the strings you read before sending them to scanf:
e.g:
c[0] = (char*)malloc(sizeof(char)*lengthOfString+1);
and only after this you can read characters in to it.
or you can use a fixed size double character array:
c[10][20];
Now c is an array of 20 strings that can be up to 9 characters long.
Amongst other problems, at the end you have:
printf("\n%s",&c[0]);
There are multiple problems there. The serious one is that c[0] is a char *, so you're passing the address of a char * — a char ** — to printf() but the %s format expects a char *. The minor problem is that you should terminate lines of output with newline.
In general, you have a mess with your memory allocation. You haven't allocated space for char *series[20] pointers to point at, so you get undefined behaviour when you use it.
You need to make sure you've allocated enough space to store the data, and it is fairly clear that you have not done that. One minor difficulty is working out what the data looks like, but it seems to be a series of lines each with 3 words and 1 number. This code does that job a bit more reliably:
#include <stdio.h>
int main(void)
{
int price[20];
int i;
char book[20][32];
char els[20][32];
char series[20][20];
const char filename[] = "read.txt";
FILE *rent = fopen(filename, "r");
if (rent == 0)
{
fprintf(stderr, "Failed to open file '%s' for reading\n", filename);
return 1;
}
for (i = 0; i < 20; i++)
{
if (fscanf(rent, "%31s%31s%19s%d", book[i], els[i], series[i], &price[i]) != 4)
break;
printf("%d. %s %s %s %d\n", i, book[i], els[i], series[i], price[i]);
}
printf("%d titles read\n", i);
fclose(rent);
return 0;
}
There are endless ways this could be tweaked, but as written, it ensures no overflow of the buffers (by the counting loop and input conversion specifications including the length), detects when there is an I/O problem or EOF, and prints data with newlines at the end of the line. It checks and reports if it fails to open the file (including the name of the file — very important when the name isn't hard-coded and a good idea even when it is), and closes the file before exiting.
Since you didn't provide any data, I created some random data:
Tixrpsywuqpgdyc Yeiasuldknhxkghfpgvl 1-967 8944
Guxmuvtadlggwjvpwqpu Sosnaqwvrbvud 1-595 3536
Supdaltswctxrbaodmerben Oedxjwnwxlcvpwgwfiopmpavseirb 1-220 9698
Hujpaffaocnr Teagmuethvinxxvs 1-917 9742
Daojgyzfjwzvqjrpgp Vigudvipdlbjkqjm 1-424 4206
Sebuhzgsqpyidpquzjxswbccqbruqf Vuhssjvcjjylcevcisdzedkzlp 1-581 3451
Doeraxdmyqcbbzyp Litbetmttcgfldbhqqfdxqi 1-221 2485
Raqqctfdlhrmhtzusntvgbvotpk Iowdcqlwgljwlfvwhfmw 1-367 3505
Kooqkvabwemxoocjfaa Hicgkztiqvqdjjx 1-466 435
Lowywyzzkkrazfyjuggidsqfvzzqb Qiginniroivqymgseushahzlrywe 1-704 5514
The output from the code above on that data is:
0. Tixrpsywuqpgdyc Yeiasuldknhxkghfpgvl 1-967 8944
1. Guxmuvtadlggwjvpwqpu Sosnaqwvrbvud 1-595 3536
2. Supdaltswctxrbaodmerben Oedxjwnwxlcvpwgwfiopmpavseirb 1-220 9698
3. Hujpaffaocnr Teagmuethvinxxvs 1-917 9742
4. Daojgyzfjwzvqjrpgp Vigudvipdlbjkqjm 1-424 4206
5. Sebuhzgsqpyidpquzjxswbccqbruqf Vuhssjvcjjylcevcisdzedkzlp 1-581 3451
6. Doeraxdmyqcbbzyp Litbetmttcgfldbhqqfdxqi 1-221 2485
7. Raqqctfdlhrmhtzusntvgbvotpk Iowdcqlwgljwlfvwhfmw 1-367 3505
8. Kooqkvabwemxoocjfaa Hicgkztiqvqdjjx 1-466 435
9. Lowywyzzkkrazfyjuggidsqfvzzqb Qiginniroivqymgseushahzlrywe 1-704 5514
10 titles read
My code reads input in the following format:
The first line has just a number
The other lines have a number and 4 strings
The first line tells the number of following lines.
After reading the file, I want to verify if the number of lines read is the same as specified in the first line. In order to achieve it, I am trying to use a variable count_lines, incrementing it at each iteration of the while loop.
FILE *fp;
char line[MAXLINELEN];
int count_lines = 0;
char city[50], continent[13], cultural[1], outdoors[1];
int total_lines, id;
...
while(fgets(line, sizeof(line), fp))
{
if (count_lines == 0)
{
sscanf(line, "%d", &total_lines);
nodes2 = calloc(sizeof(node), total_lines);
}
else if (sscanf(line, "%d %s %s %s %s", ...)
{
/* code (previously some malloc and strcopy stuff, but the error occurs even without this part of the code) */
}
else
{
/* code */
}
count_lines++;
printf("point \n");
printf("%d\n", count_lines);
}
Data example:
-bash-4.1$ cat places
3
1 City1 Continent1 Y Y
2 City2 Continent1 Y Y
3 City3 Continent1 Y N
However, this is the output of running the code:
point
1
point
1
point
1
point
1
point
1
point
1
point
1
point
1
point
1
point
1
point
1
point
1
I verified that the problem has to do with the else if part. If I comment this part, the counting works correctly. However, I could not figure out why this is happening.
What's wrong with the code?
Note: As this is part of an assignment, I cannot post the whole code.
I omitted irrelevant parts with a /* code */ comment.
strcpy copies second argumet to first argument. In your code you seem to assume the reverse
char *cont_temp = malloc(strlen(continent) + 1);
strcpy(city, cont_temp);
In this case you are copying from cont_temp to city. But cont_temp at this point contains garbage, while city contains the data you just read from file. That's one of the problems. Apparently it should be
strcpy(cont_temp, city);
shouldn't it?
However, I don't understand why you use strlen(continent) to determine the memory size, but then suddenly switch to working with city. That's another problem.
The problem is that cultural and outdoors have size 1, so there's no enough space for the new line character. Defining them with size 2 solves the problem.
I'm new to C and I see plenty of example of reading a file one word at a time but I'm trying to make a function that is given a line of text(actually a list of filenames) and it needs to read a word(filename) at a time.
Eg. I call the function, words("file1.c file2.c file3.txt");
And the function needs to read each word(filename) and put it through another function.
So far I've got:
void words(char* line) {
char buf[100];
while (!feof(line)) {
fscanf(line,"%s",buf);
printf("current word %s \n", buf);
}
}
But this won't compile. I get "passing argument 1 of ‘feof’ from incompatible pointer type"
edit So this is the code I've come up with. It seems to work fine if I called it with words("test1 test2 test3 test4 "); but if the last character is not a space then it has an error in the out put. eg ("test1 test2 test3 test4");
char buf[100];
int word_length = 0;
int n;
while((sscanf(line + word_length,"%s",buf, &n)) == 1) {
printf("current word %s \n", buf);
word_length = word_length + strlen(buf) + 1;
}
What I am doing wrong?
The fscanf and feof functions work on files.
The corresponding function for strings is sscanf.
The return value from sscanf can be used to check whether you managed to scan anything from the string and how far into the string you should look for the next word.
Edit:
Good effort. There are two problems left. First, if there are multiple spaces between words your code will fail. Also, the + 1 will move you past the null terminator if there is no space after the last word.
The second problem can be solved by not adding a +1. That means that the next item will be scanned right after the previous one ends. This is not a problem because scanf will skip initial whitespace.
The problem with multiple spaces can be solved by finding how far into the string the next token starts using strstr.
Because strstr returns a pointer I switched to using a pointer instead of an index to keep track of progress through the string.
char *ptr = line;
while((sscanf(ptr,"%s",buf)) == 1) {
printf("current word %s \n", buf);
ptr = strstr(ptr, buf); // Find where the current word starts.
ptr += strlen(buf); // Skip past the current word.
}