I want to input the following lines from file("input.txt") :
a 1,2,3,4,5,6
b 1,8
(i.e a character followed by a white-space and then an array separated by comma)
I have tried following code:
int main(int argc, char *argv[])
{
std::vector<int> arr;
FILE *file = fopen("input.txt","r");
while(!feof(file))
{
for(int i = 0; i < arr.size(); i++)
{
fscanf(file,"%s %d,",str,&arr[i]);
}
}
}
Let me know correct way to doing this, as it is showing garbage values
First of all Manisha, you are witnessing unusual code behavior because the while loop, which you have used, never stops. Let me tell you why in a very simple way. The stopping condition you have specified in the while loop, i.e, feof() indicates if one has tried to read past the end of file. But you can never read PAST the end of the file, which means the while loop would never stop.
Find an alternative way to read through the file. There are lots of other ways, one of which I have shown below:
while (fgets(line, sizeof(line), file)) {
/* note that fgets doesn't strip the terminating \n(new line character) */
...
}
if (ferror(file)) {
/* IO failure */
} else if (feof(file)) {
/* format error (not possible with fgets, but would be with fscanf) or end of file */
} else {
/* format error (not possible with fgets, but would be with fscanf) */
}
Is this supposed to be C or C++? You're using a C++ data type (std::vector), but C I/O routines. You haven't specified a type for str, either.
Assuming you mean to use C I/O routines, you could do something like this:
char str[SOME_LENGTH+1]; // where SOME_LENGTH is how big you expect the string to be
/**
* Get the string at the beginning of the line; scanf should return 1
* on a successful read.
*/
while ( scanf( "%s", str ) == 1 )
{
size_t i = 0;
/**
* Read the sequence of integers and commas. We consume the character
* immediately following the integer, but don't assign it (the %*c
* specifier). So we'll consume the comma or newline following
* the integer. Since a letter isn't part of a decimal integer,
* we'll stop scanning at the beginning of the next line.
*/
while( scanf( "%d%*c", &arr[i++] ) == 1 )
; // empty loop
}
Note: this assumes your input is well-behaved, and that there are no spaces between the number and following comma (i.e., you don't have something like 1, 2 , 3).
Related
I am simply trying to read all the information from a file and print it back to the screen, but it only prints one row of info.
Also, if you notice from the 3rd row in the txt file the last column has no ID info, and before when I had my code formatted a different way it was reading up until that 3rd row and then became an infinite loop because the ID was non-existent.
However, as it is now it only reads the first row of information from the text file and then comes to a stop and I can't figure out why.
NOTE: I have certain constraints, and I am only able to be used fscanf() to read from the file.
Code:
/*
Room# Equipment Bed# Personnel PatientID (these headings are not supposed to be in the text file)
230 respirator 2 none 1000212,1000217
231 none 4 nurse 1000214
232 none 2 doctor
233 none 1 nurse 1000219
*/
#include <conio.h>
#include <stdio.h>
typedef struct Rooms
{
int roomNum;
char respirator[11];
int bedNum;
char personnel[7];
int currPatients[5];
} rooms;
void aiAssign();
int main(void)
{
aiAssign();
getch();
return 0;
}
void aiAssign()
{
rooms build1;
FILE* rPtr = fopen("rooms.txt", "r");
while(fscanf(rPtr,"%d\t%s\t%d\t%s\t", &build1.roomNum, build1.respirator, &build1.bedNum, build1.personnel) == 4)
{
int i=0;
while(fscanf(rPtr,"%d%*c ", &build1.currPatients[i++]) == 1);
printf("%d %s %d %s", build1.roomNum, build1.respirator, build1.bedNum, build1.personnel);
for (i=0;i<2;i++)
{
if (build1.currPatients[i] >= 1000000)
{
printf("\t%d", build1.currPatients[i]);
}
}
printf("\n");
}
fclose(rPtr);
}
Reading with fscanf() is doable (if you are very careful) but is NOT the correct way to approach this problem. Why?
When reading with fscanf() all conversion specifiers except %c, %[..] and %n discard leading whitespace. The '\n' marking the end of the line is whitespace. So reading with fscanf() you have no way to prevent reading past the '\n' and extracting data from the following line if you attempt to read with more conversions specifiers than there is data for (unless you are very careful)
Normally, you would read the entire line-at-a-time with fgets() or POSIX getline(), then you can trivially pass the line to sscanf() to attempt to extract all data using a single format-string. That way the end-of-line is respected with each read and there is no possibility of reading past the end of line.
That said, it is doable. Before looking at how, let's look at how you will store your data. Unless you simply want to separate and print the data, you will need an array of rooms to store each line (record) worth of data. You should declare the array in main() and pass it as a parameter to aiAssign(). You can declare the array as follows:
#define MAXROOMS 10 /* if you need a constant, #define one (or more) */
...
int main (int argc, char **argv)
{
rooms build1[MAXROOMS] = {{ .roomNum = 0 }}; /* array of rooms, init all zero */
size_t nrooms = 0; /* number of rooms */
(note: it is imperative you initialize the array of struct all zero because your struct Rooms does not contain a counter for the number of elements stored in you currPatients array. Failing to initialize, you will have no way to access the stored values without invoking Undefined Behavior)
Generally, you also want to open and validate your file is open for reading in the calling function and pass the open FILE* pointer as a parameter to the function to use. If you can't open and validate the file is open in the caller, there is no reason to make your function call to begin with.
Additionally, do not hardcode filenames in your code. Pass the filename as an argument to your program (that's what int argc, char **argv are for) or prompt the user and have them input the filename. You should not have to recompile your program just to read from a different input file. You can take the filename as the first argument to your program, or read from stdin by default if no arguments are given as follows:
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
Your aiAssign() has another fatal flaw. With a return type of void, you have no way of communicating how many rooms of data were successfully read from the input file back to the calling function (main() here). For any function that needs to return a positive value, a count, or a length, the choice of size_t as the return type of size_t makes sense and generally, but not always, offers a greater range than int. For example, the prototype for your function, incorporating all that has been discussed above could be:
/* pass open FILE* to read, and pointer to array of rooms to fill
* returns number of rooms of data stored in array
*/
size_t aiAssign (FILE* fp, rooms *r);
Using fscanf() To Read The File
With the preliminaries out of the way, let's look at how you can read your input file with fscanf(). For the reasons related to the end-of-line and whitespace discussed above, you cannot use a single format string to read each line entirely. At best you have to break the read of each line worth of data up into two calls to fscanf(), one to read all information through the .personnel member of your struct, and the second call to read the .currPatients[] array.
In between the two calls you must check for EOF (an input failure -- where EOF is reached before the first valid conversion takes place). You must also scan forward in the input stream using fgetc() (or you can use fscanf() with the %c conversion, up to you) to locate either the next non-whitespace character or the '\n' manually. You can only attempt to read data for the .currPatients[] if additional characters exist in the line. Otherwise, since the %d conversion specifier will ignore leading whitespace, it will happily read and discard the '\n' and begin reading the roomNum member from the following line.
Once you have made the determination and either made or skipped the second call to fscanf(), you need a way of determining if you read valid data. Here, given your input file, what you really care about is that at least four successful conversions took place in the first call to fscanf() and from zero to five conversions took place reading the currPatients[] array data. That gives a range of valid returns from four to nine conversions.
(with any input function you do that by checking the return -- if you take nothing else from this answer, understand you cannot use any input function correctly unless you check the return to validate success or failure of the input)
Putting it altogether for your aiAssign() function, you could do something similar to:
/* pass open FILE* to read, and pointer to array of rooms to fill
* returns number of rooms of data stored in array
*/
size_t aiAssign (FILE* fp, rooms *r)
{
size_t n = 0; /* no. of rooms counter */
/* while array not full */
while (n < MAXROOMS) {
int haspts = 0, /* flag indicating if patients present to read */
rtn1 = 0, /* return for first fscanf */
rtn2 = 0; /* return for second fscanf */
/* read roomNum, respirator, bedNum and personnel */
rtn1 = fscanf (fp, "%d %10s %d %6[^ \t\n]", &r[n].roomNum, r[n].respirator,
&r[n].bedNum, r[n].personnel);
if (rtn1 == EOF) /* if input-failure (no conversions before EOF), break */
break;
/* loop to find next character or newline */
for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
if (!isspace(c)) { /* if non-whitespace character */
ungetc (c, fp); /* put it back in input stream */
haspts = 1; /* set has patients flag true */
break;
}
else if (c == '\n') /* if newline, leave has patients false */
break;
if (haspts) /* if patient info to read, attempt to fill array */
rtn2 = fscanf (fp, "%d,%d,%d,%d,%d", &r[n].currPatients[0],
&r[n].currPatients[1], &r[n].currPatients[2],
&r[n].currPatients[3], &r[n].currPatients[4]);
/* no patients or first fscanf all conversions took place */
if ((rtn1 && !rtn2) || (rtn1 == 4)) {
/* validate at least 4 up to 9 total conversions took place */
switch (rtn1 + rtn2) {
case 9:
case 8:
case 7: /* case fall-through intentional */
case 6:
case 5:
case 4:
n++; /* increment counter */
break;
}
}
/* remove all characters up to '\n' */
if (haspts)
for (int c = fgetc(fp); c != '\n' && c != EOF; c = fgetc(fp)) {}
}
return n; /* return number of rooms */
}
(note: the haspts (short for "has patients") flag is used to indicate whether the second call to fscanf() for currPatients data is needed. Note also you cannot read string data with any of the scanf() family of functions without also including the field-width modifier to prevent reading more characters than your array can hold, resulting in a buffer overrun. Without the field-width modifier, reading string data with scanf() is no safer than using gets(), see Why gets() is so dangerous it should never be used!)
A short example using the function to read the data from your file using only fscanf() could be done as:
#include <stdio.h>
#include <ctype.h>
#define MAXROOMS 10 /* if you need a constant, #define one (or more) */
typedef struct Rooms {
int roomNum;
char respirator[11];
int bedNum;
char personnel[7];
int currPatients[5];
} rooms;
/* pass open FILE* to read, and pointer to array of rooms to fill
* returns number of rooms of data stored in array
*/
size_t aiAssign (FILE* fp, rooms *r)
{
size_t n = 0; /* no. of rooms counter */
/* while array not full */
while (n < MAXROOMS) {
int haspts = 0, /* flag indicating if patients present to read */
rtn1 = 0, /* return for first fscanf */
rtn2 = 0; /* return for second fscanf */
/* read roomNum, respirator, bedNum and personnel */
rtn1 = fscanf (fp, "%d %10s %d %6[^ \t\n]", &r[n].roomNum, r[n].respirator,
&r[n].bedNum, r[n].personnel);
if (rtn1 == EOF) /* if input-failure (no conversions before EOF), break */
break;
/* loop to find next character or newline */
for (int c = fgetc(fp); c != EOF; c = fgetc(fp))
if (!isspace(c)) { /* if non-whitespace character */
ungetc (c, fp); /* put it back in input stream */
haspts = 1; /* set has patients flag true */
break;
}
else if (c == '\n') /* if newline, leave has patients false */
break;
if (haspts) /* if patient info to read, attempt to fill array */
rtn2 = fscanf (fp, "%d,%d,%d,%d,%d", &r[n].currPatients[0],
&r[n].currPatients[1], &r[n].currPatients[2],
&r[n].currPatients[3], &r[n].currPatients[4]);
/* no patients or first fscanf all conversions took place */
if ((rtn1 && !rtn2) || (rtn1 == 4)) {
/* validate at least 4 up to 9 total conversions took place */
switch (rtn1 + rtn2) {
case 9:
case 8:
case 7: /* case fall-through intentional */
case 6:
case 5:
case 4:
n++; /* increment counter */
break;
}
}
/* remove all characters up to '\n' */
if (haspts)
for (int c = fgetc(fp); c != '\n' && c != EOF; c = fgetc(fp)) {}
}
return n; /* return number of rooms */
}
int main (int argc, char **argv)
{
rooms build1[MAXROOMS] = {{ .roomNum = 0 }}; /* array of rooms, init all zero */
size_t nrooms = 0; /* number of rooms */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
nrooms = aiAssign (fp, build1); /* read room data, save return */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
if (!nrooms) { /* no rooms of data read */
fputs ("error: nothing read from file.\n", stderr);
return 1;
}
for (size_t i = 0; i < nrooms; i++) { /* output all data */
printf ("%3d %-11s %d %-7s ", build1[i].roomNum, build1[i].respirator,
build1[i].bedNum, build1[i].personnel);
/* possible due to initialization of array all zero at declaration */
for (int j = 0; build1[i].currPatients[j] && j < 5; j++)
printf (j ? ":%d" : "%d", build1[i].currPatients[j]);
putchar ('\n');
}
/* only hold terminal window open on windows */
#if defined (_WIN32) || defined (_WIN64)
getchar(); /* do not use getch() (100% non-portable DOS conio.h required) */
#endif
return 0;
}
(note: the inclusion of the ctype.h header to help when checking for whitespace with isspace() and note the removal of the archaic DOS header conio.h. You should simply use getchar() to hold the terminal open on windows instead of getch(). Including conio.h makes your program 100% NON-portable to anything but windows)
Example Use/Output
With your data in rooms.txt including the entire first line with your comment, you would have:
$ ./bin/buildrooms dat/rooms.txt
230 respirator 2 none 1000212:1000217
231 none 4 nurse 1000214
232 none 2 doctor
233 none 1 nurse 1000219
(note: the currPatients array data is output separate by ':' instead of ',')
While you can read your data with fscanf() (if you are very careful), you really shoudn't. fgets()/sscanf() provides a much easier and more robust approach. However, as an exercise, this is a pretty darn good one to teach you the consideration that go into selection and using the proper input functions as well as on the use (and limitations) of fscanf().
There is a lot involved, so take the review of the code slowly, line-by-line, expression-by-expression and make sure you understand why the code is written the way it is. If you get stuck, drop a comment below and I'm happy to help further.
I think you are over complicating it, if your only purpose is to read the file line by line and then print it to the screen, you could use the simpler functions open, read and write. Check them. Define a buffer and use it to read, then use stdout file descriptor to write the data you read in the buffer . Keep the looping till the number of bytes read returned by the read function is less than the buffer size. The file descriptor for the stdout is 1. Here is a link that explains how these functions work
https://www.google.es/amp/s/www.geeksforgeeks.org/input-output-system-calls-c-create-open-close-read-write/amp/
If you need to convert the raw text file to those data structures then you a more intelligent processing.
The functions fscanf and printf are higher level built on top of these ones I suggest you. They receive formats strings which uses as guidelines to automate processing like the integer conversion from ascii when you specify %d . This was an example .
I have a program to reads and prints the phrases in a csv file using C programming language. The program to output the phrase after the set number of commas within the csv file (point variable) . The csv file is a very long dataset. The C code below has a problem where after a certain amount of outputs it gives an error and closes the file before it reaches the end of the file. In addition to that I want to add another character like (point) called (point2) however instead of it starting from the 10th comma I want the new point to start from 8 and then gets incremented by 7 again.
#include<stdio.h>
// fetch the phrase based on the point
void fetchPhrase(int point){
// create FILE pointer variable which will access the file
FILE *fp;
// read character from file
char ch;
int flagEndFile = 0;
// store phrase that is readed from file
char phrase[100];
// work as index for phrase
int index = 0;
// track the ","
int counter = 0;
// open the file in read mode
fp = fopen("input.csv", "r");
// check whether file is opened or not
if(fp == NULL){
printf("File not found!");
exit(1);
}
// read the character untill the file is not end
while((ch = fgetc(fp)) != EOF){
// check whether character is ',' or not
if(ch == ','){
// increment the counter
counter++;
}
// when we get the "," as well as correct point
if(ch == ',' && counter == point){
// read the phrase until the program does not found ","
while((ch = fgetc(fp)) != ','){
// if file is ended then break the loop
if(ch == EOF){
break;
}
// otherwise store phrase into the phrase array
phrase[index++] = ch;
}
// break the loop
break;
}
}
// store null into the character array
phrase[index] = '\0';
// check whether the point is found or not in the file
if(ch == EOF && phrase[0] == '\0'){
// point is not found in the file
printf("There is no point in the 'input' file");
}else{
// otherwise print the phrase
printf("\n Price is: %s", phrase);
}
}
void main(){
// create FILE pointer variable which will access the file
FILE *fp;
// store usr input
int point = 10;
// this loop run untill the user does not enter -999.
while(point != -999){
// call the function which display the phrase
fetchPhrase(point);
//Nexct price value is 7 commas after
point = point + 7;
//printf("The price is %d\n", point);
// get the input from the user
//printf("\nAfter what comma do you want the code to print: ");
//scanf("%d", &point);
}
// close the file
fclose(fp);
}
The big issue I see in fetchPhrase() is when you find the ',' you want, you read the comma as part of phrase. The next big issue I see is since you are reading with fgetc(), you are not checking for '\n'. So for example when you update point = point + 7 and there are less than that number of fields left in the current record (line), you then read and ignore the '\n' and start attempting to read fields from the next line. Since a '\n' isn't a ',' you never increment counter so your field count will be off when you begin reading from the next line.
Now it is difficult to know how to handle all your requests for "point" if it wraps and begins reading from the next line without a detailed explanation, but returning if you ask for more fields than are in the line sounds reasonable and could be done with something similar to:
#define PHRASE 100
int fetchPhrase (int point, FILE *fp)
{
char phrase[PHRASE] = ""; /* buffer to hold phrase */
int ch, /* current character */
index = 0, /* phrase index */
counter = 0; /* separator counter */
/* read all chars in line */
while ((ch = fgetc (fp)) != '\n' && ch != EOF) {
/* check if wanted field (point) found */
if (counter == point) {
/* protect phrase bounds, while not next separator */
while (index + 1 < PHRASE && ch != ',' && ch != '\n' && ch != EOF) {
phrase[index++] = ch; /* store character in phrase */
ch = fgetc (fp);
}
phrase[index] = '\0'; /* nul-terminate */
break; /* break read loop */
}
if (ch == ',') /* comma found */
counter++; /* increment counter */
}
if (counter == point && phrase[0] != 0) /* phrase found */
printf ("\n Price is: %s\n", phrase);
else
fputs ("There is no point in line.\n", stderr);
return ch; /* return stream state good (>0) if not EOF */
}
(note: untested, no sample data provided, but the points (fields) are zero indexed, meaning if you ask for point == 0 it will return the 1st field in the line, point == 1 will be the 2nd field (after the 1st ','), and so on...)
That way you are only reading characters in the field, not the ','. Also note phrase is nul-terminated immediately after it is filled.
Note: the other changes. Do not hardcode filenames in functions. Do not use MagicNumbers. Pass the open FILE* stream as a parameter to your function instead of hardcoding input.csv in your function. You should not have to recompile your code just to read from a different filename. For your MagicNumber 100 in phrase[100], #define a constant any time one is needed. That provides a convenient place at the beginning of your code to make adjustments without having to pick through the body of your code.
You must protect your array bounds when filling phrase with characters. If your phrase is longer than your array, you need to stop adding characters while preserving room for the nul-terminating character. Checking index + 1 < PHRASE does that for you.
Any time your function can succeed or fail, you must declare a meaningful return type for the function to indicate success/failure. In this case, since you want to read from the file multiple times, you need a way to indicate whether EOF was reached. Returning an int for true / false is a simple way to indicate success or failure and can be used to indicate whether EOF has been reached by returning the result of ch != EOF.
The correct use of main()
Unless you are programming in a freestanding environment (without the benefit of any OS), in a standards conforming implementation, the allowable declarations for main for are int main (void) and int main (int argc, char *argv[]) (which you will see written with the equivalent char **argv). See: C11 Standard - §5.1.2.2.1 Program startup(p1). See also: What should main() return in C and C++? In a freestanding environment, the name and type of the function called at program startup are implementation-defined. See: C11 Standard - 5.1.2.1 Freestanding environment
Passing the Filename to Read as an Argument
With int main (int argc, char **argv), you can pass information into your program from the command line. argc is your argument-count telling you the number of command line arguments provided (note: there is always 1 argument, argv[0] always holds the program name). So the first argument passed to the program is argv[1] and so on. argv is an array of pointers to the arguments which are nul-terminated strings with the next pointer after the last string set to NULL as a sentinel.
You can check for the filename passed as an argument to your program (or read from stdin by default) using the following:
/* pass filename as 1st argument to program, or read stdin by default */
int main (int argc, char **argv)
{
int point = 10;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
Now you can pass the open FILE* pointer to your function as an argument -- your function can rely on the fact the file has been opened and validated open for reading in the caller:
fetchPhrase (point, fp);
You can use also use the return to determine if EOF has been reached:
while (fetchPhrase (point, fp) != EOF) {
...
}
Using a Line-Oriented Approach
While your use of fgetc() is fine, it complicates reading records (lines) in your input file. If you instead read each line with a line-oriented input function such as fgets() or POSIX getline(), then processing requests for offsets from fields in the file would be limited to the fields contained in the line and would eliminate your testing of '\n' to constrain your field requests to a single record.
Those were the thoughts I had on your code. Look things over and let me know if you have further questions.
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);
I'm trying to read multiple lines of vertices with varying length using fgets and sscanf.
(1,6),(2,6),(2,9),(1,9)
(1,5)
My program goes into an infinite loop stuck within the first vertex.
char temp3[255];
while(fgets(temp3, 255, fp)!= NULL){
printf("Polygon %d: ", polycount);
while(sscanf(temp3, "(%d,%d)", &polygonx[polycount][vertcount], &polygony[polycount][vertcount]) != EOF){
sscanf(temp3, ",");
printf("(%d,%d),",polygonx[polycount][vertcount], polygony[polycount][vertcount]);
vertcount++;
}
vertcounts[polycount] = vertcount;
vertcount = 0;
polycount++;
}
I must be able to feed the x and y values of the vertices into the polygon arrays, so i'm stuck with using sscanf. I'm also having a problem since I cant find anything on the internet that scans varying numbers of elements per line.
It's because this
while(sscanf(temp3, "(%d,%d)",
&polygonx[polycount][vertcount], &polygony[polycount][vertcount]) != EOF)
{
}
is never going to be true I think, because scanf() returns the number of parameters succesfuly scanned, I would do this instead
while(sscanf(temp3, "(%d,%d)",
&polygonx[polycount][vertcount], &polygony[polycount][vertcount]) == 2)
{
}
Your code doesn't work because it does not satisfy the condition for sscanf() to return EOF, the following is from the manual page referenced at the end
The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream (see ferror(3)) is set, and errno is set to indicate the error.
So it appears that you are not reaching the end if input before the first successful conversion or a matching failure occurs, which makes sense according to the contents of the file. And the second part applies only to file streams of course.
And instead of the sscanf(temp3, ",") which doesn't do what you think, you could do it like this
next = strchr(temp3, ',');
if (next != NULL)
temp3 = next + 1;
else
/* you've reached the end here */
This is a suggestion on how to parse this file
#include <stdio.h>
#include <string.h>
int
main(void)
{
const char temp3[] = "(1,6),(2,6),(2,9),(1,9)\n(1,5)";
char *source;
int x, y;
int count;
source = temp3;
while (sscanf(source, "(%d,%d)%*[^(]%n", &x, &y, &count) == 2)
{
/* this is just for code clarity */
polygonx[polycount][vertcount] = x;
polygony[polycount][vertcount] = y;
/* Process here if needed, and then advance the pointer */
source += count;
}
return 0;
}
The "%n" specifier captures the number of characters scanned so far, so you can use it to advance the pointer to the las position scanned in the source string.
And the "%*[^(" will skip all characters until the next '(' is found.
Please refer to sscanf(3) for more information on the "%n" specifier, and the %[ specifier.
If successfully read sscanf will return 2 in this case . sscanf returns numbers of variables filled.
Check if it returns 2 which will indicate success here .
while(sscanf(temp3,"(%d,%d)",&polygonx[polycount][vertcount],&polygony[polycount]][vertcount]) != EOF)
Instead of this , check like this -
while(sscanf(temp3,"(%d,%d)%*c",&polygonx[polycount][vertcount],&polygony[polycount]][vertcount])== 2)
^ to exclude the comma after it
also to ignore ',' after the coordinates , you use -
sscanf(temp3, ",");
is not correct . In the above sscanf you can read it and discard it as well by using %*c specifier.
for example:
file_ptr=fopen(“data_1.txt”, “r”);
how do i find number of lines in the file?
You read every single character in the file and add up those that are newline characters.
You should look into fgetc() for reading a character and remember that it will return EOF at the end of the file and \n for a line-end character.
Then you just have to decide whether a final incomplete line (i.e., file has no newline at the end) is a line or not. I would say yes, myself.
Here's how I'd do it, in pseudo-code of course since this is homework:
open file
set line count to 0
read character from file
while character is not end-of-file:
if character in newline:
add 1 to line count
read character from file
Extending that to handle a incomplete last line may not be necessary for this level of question. If it is (or you want to try for extra credits), you could look at:
open file
set line count to 0
set last character to end-of-file
read character from file
while character is not end-of-file:
if character in newline:
add 1 to line count
set last character to character
read character from file
if last character is not new-line:
add 1 to line count
No guarantees that either of those will work since they're just off the top of my head, but I'd be surprised if they didn't (it wouldn't be the first or last surprise I've seen however - test it well).
Here's a different way:
#include <stdio.h>
#include <stdlib.h>
#define CHARBUFLEN 8
int main (int argc, char **argv) {
int c, lineCount, cIdx = 0;
char buf[CHARBUFLEN];
FILE *outputPtr;
outputPtr = popen("wc -l data_1.txt", "r");
if (!outputPtr) {
fprintf (stderr, "Wrong filename or other error.\n");
return EXIT_FAILURE;
}
do {
c = getc(outputPtr);
buf[cIdx++] = c;
} while (c != ' ');
buf[cIdx] = '\0';
lineCount = atoi((const char *)buf);
if (pclose (outputPtr) != 0) {
fprintf (stderr, "Unknown error.\n");
return EXIT_FAILURE;
}
fprintf (stdout, "Line count: %d\n", lineCount);
return EXIT_SUCCESS;
}
Is finding the line count the first step of some more complex operation? If so, I suggest you find a way to operate on the file without knowing the number of lines in advance.
If your only purpose is to count the lines, then you must read them and... count!