I am working on an assignment that puts takes a file containing a recipe and creates an instance of a struct to store the information. This is the format that my struct follows:
struct Dinner
{
char* recipeName;
unsigned numMainDishIngredients;
char** mainDishIngredients;
unsigned numDessertIngredients;
char** DessertIngredients;
};
I need to figure out how to use a read in a file which will be structured as follows:
The first line will contain the name of the recipe, the second line will be the number of ingredients in the main dish, then the next lines will each contain one ingredient that is in the main dish until one blank line is hit. The line following the blank line will contain the number of ingredients in the dessert and the following lines will each contain a dessert ingredient.
An example is as follows:
Pizza and Ice Cream
4
Dough
Cheese
Sauce
Toppings
3
Cream
Sugar
Vanilla
I am mostly unsure of how to read into the char** types. So far this is all I have:
struct Dinner* readRecipe(const char* recipeFile)
if (!recipeFile)
{
return NULL;
}
File* file = fopen(recipeFile, "r");
if (!file)
{
return NULL;
}
char recipeName[50]; // specified that strings wont exceed 49 chars
int numMainIngredients, numDessertIngredients;
fscanf(file, "%s, %d", &recipeName, numMainIngredients);
...
}
Basically I do not know how to read multiple lines of a file into an array type in a structure and I would really appreciate any tips on how to do this.
Reading from a file is pretty straight forward. Most of the std functions are designed to read a single line from the file, then move to the next line automatically. So all you really need to do, is loop.
I recommend that you write the following
#define MAXCHAR 256
char[MAXCHAR] line;
while(fgets(line, MAXCHAR, file) != NULL)
{
// line now has the next line in the file
// do something with it. store it away
// use atoi() for get the number?
// whatever you need.
}
That is, we use fgets() to grab the next line in the file; and if we loop that a bunch; it will read until the end of file (EOF).
Note that I used fgets() instead of fscanf(). fgets() is a safer and more efficient option than fscanf. Granted, it does not come with the fancy ability to specify line formatting and such; but it's not too difficult to do that on your own.
edit: I mixed up my languages pretty badly.. fixed it.
Related
So i have task to output to one file certain things from another file... Lets say input file is in format
(NAME#SURNAME AGE) with 100 lines and i have to output persons that are older then 15 in this format
(NAME AGE SURNAME) and i have something like this in struct
struct person
{
char name[10];
char surname[10];
int age;
};
Can I, and if I can how can I fread my input file to that struct (file is binary)
If you want more help about the code please do a first version and edit you question, then add a comment and the community will help you.
But if you are looking for the methodology you can go with something like that :
Open the file you want to extract data in read mode (e.g. fopen(filename, "r"))
Check if the file was correctly open
Use a while and take N characters with fgets() or use getline() (Note that getline() is in ISO/IEC TR 24731-2:2010 extension (see n1248).).There is lots of threads about how to read line by line in a file (e.g. Going through a text file line by line in C)
Now you are looking for a way to split your line with delimiters, your can use strtok(), strsep()/strpbrk() or you can do your own implementation of a simple string delimiter (Maybe look this question)
Next the AGE part, you wan to only copy the line with an AGE superior to 15, so use strtol() to convert your string to long int then check the value.Don't use atoi() this function is deprecated).
The atoi() function has been deprecated by strtol() and should not be used in new code.
source Mac OS X Manual Page for atoi(3)
Then you need to create and/or write to a file. You can use fopen(newFilename, "w"), check the file descriptor the function give you in return, if it's ok you can add line with fprintf() (e.g. fprintf(fd, "%s %s %s\r\n", name, age, surname);).
If this is one of your first project/exercise in c take your time to properly understand the function you use and always check the returned values before using them.
I have to parse a file that would look something like this
String|OtherString|1234|0
String2|OtherString2|4321|1
...
So, I need to go through every line of the file and take each seperate token of each line.
FILE *fp=fopen("test1.txt","r");
int c;
char str1[500];
char str2[500];
int num1=0;
int num2;
while((c=fgetc(fp))!=EOF){
fscanf(fp, "%s|%s|%d|%d", &str1[0], &str2[0], &num1, &num2);
}
fclose(fp);
There's more to it, but these are the sections relevant to my question. fscanf isn't working, presumably because I've written it wrong. What's supposed to happen is that str1[500] should be set to String, in this case, str2 to OtherString, etc. It seems as though fscanf isn't doing anything, however. Would greatly appreciate some help.
EDIT: I am not adamant about using fgetc or fscanf, these are just what I have atm, I'd use anything that would let me do what I have to
strtok() in a loop will work for you. The following is a bare bones example, with very little error handling etc, but illustrates the concept...
char strArray[4][80];
char *tok = NULL;
char *dup = strdup(origLine);
int i = 0;
if(dup)
{
tok = strtok(dup, "|\n");
while(tok)
{
strcpy(strArray[i], tok);
tok = strtok(NULL, "|\n");
i++;
}
free(dup);
}
If reading from a file, then put this loop inside another while loop that reads the file, line by line. Functions useful for this will include fopen(), fgets() and fclose(). One additional feature that should be considered for code that reads data from a file is to determine the number of records (lines) in the file to be read, and use that information to create a properly sized container with which to populate with the parsing results. But this will be for another question.
Note: fgetc() is not suggested here as it reads one char per loop, and would be less efficient than using fgets() for reading lines from a file when used in conjunction with strtok().
Note also, in general, the more consistently a file is formatted in terms of number of fields, content of fields, etc. the least complicated a parser needs to be. The inverse is also true. The less consistently formatted input file requires a more complex parser. For example, for human entered line data, the parser required is typically more complicated than say one used for a computer generated set of uniform lines.
I am writing a program which reads a file with almost 2 million lines. The file is in the format integer ID tab with an artist name string.
6821361 Selinsgrove High School Chorus
10151460 greek-Antique
10236365 jnr walker & the all-stars
6878792 Grieg - Kraggerud, Kjekshus
6880556 Mr. Oiseau
6906305 stars on 54 (maxi single)
10584525 Jonie Mitchel
10299729 エリス レジーナ/アントニオ カルロス ジョビン
Above is an example with some items from the file (not some lines do not follow the specific format). My program work file until it gets to the last line from the example then it endlessly prints エリス レジーナ/アントニオ カルロス ジョビ\343\203.
struct artist *read_artists(char *fname)
{
FILE *file;
struct artist *temp = (struct artist*)malloc(sizeof(struct artist));
struct artist *head = (struct artist*)malloc(sizeof(struct artist));
file = fopen("/Users/Daniel/Library/Developer/Xcode/DerivedData/project_Audioscrobbler_Artists-hgwyqpinuoxayzbmvarcjxryqnrz/Build/Products/Debug/artist_data.txt", "r");
if(file == 0)
{
perror("fopen");
exit(1);
}
int artist_ID;
char artist_name[650];
while(!feof(file))
{
fscanf(file, "%d\t%65[^\t\n]\n", &artist_ID, artist_name);
temp = create_play(artist_ID, artist_name, 0, -1);
head = add_play(head, temp);
printf("%s\n", artist_name);
}
fclose(file);
//print_plays(head);
return head;
}
Above is my code for reading from the file. Can you please help explain what is wrong?
As the comments indicate, one problem is with while(!feof(file)) The linked content will explain in detail why this is not a good idea, but in summary, quoting from one of the answers in the link:
(!feof(file))...
...is wrong because it tests for something that is
irrelevant and fails to test for something that you need to know. The
result is that you are erroneously executing code that assumes that it
is accessing data that was read successfully, when in fact this never
happened. - Kerrek SB
In your case, this usage does not cause your problem, but as Kerrek explains might happen, masks it.
You can replace that with fgets(...):
char lineBuf[1000];//make length longer or shorter for your purpose
file = fopen("/Users/Daniel/Library/Developer/Xcode/DerivedData/project_Audioscrobbler_Artists-hgwyqpinuoxayzbmvarcjxryqnrz/Build/Products/Debug/artist_data.txt", "r");
if(!file) return -1;
while(fgets (lineBuf, sizeof(lineBuf), file))
{
//process each line here
//But processing Japanese characters
//will require special considerations.
//Refer to the link below for UNICODE tips
}
Unicode in C and C++...
In particular, you will need to use variable types that are sufficient for containing the different size characters you will be processing. The link discusses this in great detail.
Here is an excerpt:
"char" no longer means character
I hereby recommend referring to character codes in C programs using a 32-bit unsigned integer type. Many platforms provide a
"wchar_t" (wide character) type, but unfortunately it is to be avoided
since some compilers allot it only 16 bits—not enough to represent
Unicode. Wherever you need to pass around an individual character,
change "char" to "unsigned int" or similar. The only remaining use for
the "char" type is to mean "byte".
Edit:
In the comments above, you state but the string it's failing on is 66 bytes long. Because you are reading into a 'char' array, the bytes necessary to complete the character were truncated one byte before including the last necessary byte. ASCII characters can be contained in a single char space. Japanese characters cannot. If you were using an array of unsigned int instead of array of char, the last byte would have been included.
OP's code failed because the result of fscanf() was not checked.
fscanf(file, "%d\t%65[^\t\n]\n", &artist_ID, artist_name);
The fscanf() read in 65 char of "エリス レジーナ/アントニオ カルロス ジョビン". Yet this string, encoded in UTF8, has a length of 66. The last 'ン' is codes 227, 131, 179 (octal 343 203 263) and only the last 2 were read. When artist_name is printed the following appears.
エリス レジーナ/アントニオ カルロス ジョビ\343\203
Now begins the problem. The last char 179 remains in in file. On the next fscanf(), it fails as char 179 does not convert into a int ("%d"). So fscanf() returns 0. Since code did not check the result of fscanf(), it does not realize artist_ID and artist_name are left over from before and so prints the same text.
As feof() is never true for the char 179 is not consumed, we have infinite loop.
The while(!feof(file)) hid this problem, but did not cause it.
The fgets() proposed by #ryyker is a good approach. Another is:
while (fscanf(file, "%d\t%65[^\t\n]\n", &artist_ID, artist_name) == 2) {
temp = create_play(artist_ID, artist_name, 0, -1);
head = add_play(head, temp);
printf("%s\n", artist_name);
}
IOWs, validate the results of *scanf().
Making a program that adds user records to text file; so far so good! Yet I ran into a problem that I can not figure out on my own.
int main()
{
FILE *fp;
struct info
{
char name[15];
char surename[15];
char gender[15];
char education[15];
} info;
char c;
int i,j,a;
struct info sem;
beginning:
scanf("%d",&a);
if (a==1)
and at this part if user chooses option one 1, program needs to check all the records per person in txt file and printf me every single person information who has bachelors education.
{
FILE *fp=fopen("info.txt", "r");
char tmp[256]={0x0};
while(fp!=NULL && fgets(tmp, sizeof(tmp),fp)!=NULL)
{
if(strstr(tmp,"bachelors"))
printf("test test");
fprintf(fp, "\n%s %s %s %s %s %s",
sem.name,
sem.surname,
sem.gender,
sem.education,);
}
if(fp!=NULL) fclose(fp);
goto beginning;
}
This code so far detects the word "bachelor" but doesn't want to print out the line where it detected it; any ideas how to solve it? Plus, any suggestions how to make sure program only checks education field and doesn't give me false positive if some one would be named Bachelors?
printf/fprintf procedures use internal buffers to avoid calling the syscall "write" everytime.
I would say that you should add a '\n' character at the end of your string to actually force flushing the buffers writing the test. Moreover your token "bachelors" has to be in the 256 first characters of your file.
Second question depends on how your file is formatted, and you are the only one who knows that.
What output do you actually get? You say it doesn't want to print out the correct lines, but does that mean it prints all lines, no lines, some but not others?
Looking at your program, I would hazard a guess that every line is being printed out. You need braces around if(strstr(tmp,"bachelors")) if you want more than one statement in the body of the if.
EDIT:
In addition to your braces problem, you are attempting to use fprintf to print back to fp, which was opened in read mode (using the r flag). You need to use read/update mode (r+) if you want to modify info.txt. However, this is probably not the way you want to do this.
Firstly, you stated you wanted to "printf" the data, which means printing to standard out. If so then you should use printf instead of fprintf. On the other hand it would seem likely that what you want to do is to read lines from one text file and print data out to another file. In that case you probably want two files.
Even with the above modifications, you will be printing the same data to file each time, since the sem struct is not being updated at all.
Finally, your fprintf format string expects six inputs and you only have four (and an erroneous trailing ,). Why?
I am new to C programming, so I am having difficulties with the problem below.
I have a text file inp.txt which contains information like the following:
400;499;FIRST;
500;599;SECOND;
670;679;THIRD;
I need to type a number and my program needs to compare it with numbers from the inp.txt file.
For example, if I type 450, it's between 400 and 499, so I need write to the word FIRST to the file out.txt
I have no idea how to convert a character array to an int.
I think you'll want these general steps in your program (but I'll leave it to you to figure out how you want to do it exactly)
Load each of the ranges and the text "FIRST", "SECOND", etc. from the file inp.txt, into an array, or several arrays, or similar. As I said in the comment above, fscanf might be handy. This page describes how to use it - the page is about C++, but using it in C should be the same http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/. Roughly speaking, the idea is that you give fscanf a format specifier for what you want to extract from a line in a file, and it puts the bits it finds into the variables you specify)
Prompt the user to enter a number.
Look through the array(s) to work out which range the number fits into, and therefore which text to output
Edit: I'll put some more detail in, as asker requested. This is still a kind of skeleton to give you some ideas.
Use the fopen function, something like this (declare a pointer FILE* input_file):
input_file = fopen("c:\\test\\inp.txt", "r") /* "r" opens inp.txt for reading */
Then, it's good to check that the file was successfully opened, by checking if input_file == NULL.
Then use fscanf to read details from one line of the file. Loop through the lines of the file until you've read the whole thing. You give fscanf pointers to the variables you want it to put the information from each line of the file into. (It's a bit like a printf formatting specifier in reverse).
So, you could declare int range_start, range_end, and char range_name[20]. (To make things simple, let's assume that all the words are at most 20 characters long. This might not be a good plan in the long-run though).
while (!feof(input_file)) { /* check for end-of-file */
if(fscanf(input_file, "%d;%d;%s", &range_start, &range_end, range_name) != 3) {
break; /* Something weird happened on this line, so let's give up */
else {
printf("I got the following numbers: %d, %d, %s\n", range_start, range_end, range_name);
}
}
Hopefully that gives you a few ideas. I've tried running this code and it did seem to work. However, worth saying that fscanf has some drawbacks (see e.g. http://mrx.net/c/readfunctions.html), so another approach is to use fgets to get each line (the advantage of fgets is that you get to specify a maximum number of characters to read, so there's no danger of overrunning a string buffer length) and then sscanf to read from the string into your integer variables. I haven't tried this way though.