I have a text file like this:
3 3 0
2 3 5
3 8 9
4 5 6
I want to put this numbers into a matrix.
The first line are the numbers of rows and columns to make the malloc for the matrix.
Then, the next lines are the numbers which are going to be on the matrix.
And I have this code:
int mat[3][3];
int i, j;
FILE *fp;
char c;
if((fp = fopen("texto.txt", "r")) == NULL){
printf("Error al abrir el archivo");
return -1;
}
for(i=0;i<3;i++){
for(j=0;j<3;j++){
mat[i][j]=0;
}
}
i = 0;
while (1)
{
if (feof(fp))
break;
fscanf (fp, "%[^\n]%d %d %d", &mat[i][0], &mat[i][1], &mat[i][2]);
i++;
}
fclose(fp);
for(i=0;i<3;i++){
for(j=0;j<3;j++){
printf("%d ", mat[i][j]);
}
printf("\n");
}
(Sorry for the spanish lines)
I dont have the malloc written becouse i just want to see if I create the matrix correctly knowing that is a 3x3.
So the problem is that I dont know how to make the matrix from the second line of the text file, and dont use the first line.
Thanks!
Read documentation of fscanf. Notice that spaces in the format control string may also skip newline characters, and that fscanf is returning the number of scanned items (which should matter to you). In some cases, you might be interested also by %n.
Be aware that your usage of fscanf is wrong (so is undefined behaviour) because %[^\n] expects an argument that you don't provide. With a good compiler like GCC, you should enable all warnings and debug info (e.g. compile with gcc -Wall -Wextra -g) and you'll get a warning.
If you want to skip the first line, start by reading every character (with fgetc) till you get EOF or '\n'. Or use fgets or getline(3) (on POSIX, like this) to read and ignore the first line.
If lines really matter, you could read every line (e.g. with fgets or getline(3)) and parse each line with e.g. sscanf or otherwise.
Related
i am new in c. So in my university, i just learn about file in c. and i got a task. If i put an empty file in my project directory, and read it. The output are symbols (i dont know what symbol it is). So here is the code, please help
player dota[100];
FILE *fp;
fp = fopen("soal09.txt", "r");
if(fp == NULL)
{
printf("Error Opening The File!!\n");
return 0;
}
else
{
while(!feof(fp))
{
fscanf(fp, "%[^ ] %d %d\n", &dota[idx].name, &dota[idx].score, &dota[idx].num);
idx++;
}
}
fclose(fp);
do
{
enter();
menu();
printf("Input your choice [1..5]: ");
scanf("%d", &choose); fflush(stdin);
if(choose == 1)
{
system("cls");
enter();
printf("%-20s %-15s %s\n", "Player Name", ": Average Score", ": Number of Playing");
printf("====================================================================\n");
for(int i = 0; i < idx; i++)
{
printf("%-20s %-15d %d\n", dota[i].name, dota[i].score, dota[i].num);
}
printf("\nPress Enter to Continue...");
getchar();
}
getchar();
return 0;
}
and the output is ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ -858993460
Thank you ^^
The end-of-file indicator that is checked by feof() is only set after a previous file I/O operation has failed. You must attempt an I/O operation to find out if you have reached the end of the file. So, with an empty file, your code attempts to read the file, the end-of-file indicator is set, no values are read into the first struct, but idx is incremented, so it looks like you have added data for a player. But the fields of the first struct are uninitialized, so you are seeing garbage. Also note that dota[idx].name is presumably an array of chars, so it decays to a pointer to char when passed to fscanf(). Using &dota[idx].name, as you have, is wrong, though it might appear to work. It does cause the compiler to emit a warning, and you should have these enabled (I always use at least gcc -Wall -Wextra -pedantic).
You should not use feof() to control file I/O loops. One simple solution is to use the return value of fscanf() to control the loop:
while(fscanf(fp, "%[^ ] %d %d\n",
dota[idx].name, &dota[idx].score, &dota[idx].num) == 3) {
idx++;
}
This will only update a player struct if three assignments are made by the call to fscanf(). But, the problem with this simple solution is that it doesn't handle malformed input well. If a line of the data file is missing a field, the struct will be incorrectly filled, and the loop will terminate, even if there are more lines in the file to read. Also, since no field width is specified for the string conversion, a long name could crash your program.
A better solution uses fgets() to read the lines of the file into a buffer, and then uses sscanf() to extract the information from the buffer:
#include <string.h> // for strcpy()
...
char buffer[1000];
int line = 0;
char temp_name[100];
int temp_score, temp_num;
while(fgets(buffer, sizeof(buffer), fp)) {
++line;
if (sscanf(buffer, "%99[^ ] %d %d\n",
temp_name, &temp_score, &temp_num) == 3) {
strcpy(dota[idx].name, temp_name);
dota[idx].score = temp_score;
dota[idx].num = temp_num;
++idx;
} else if (buffer[0] != '\n') {
fprintf(stderr, "Bad input in line %d\n", line);
}
}
Here, a generous buffer is declared to accept a line of text from the file, and temporary variables are declared to hold the values to be stored in the struct fields. I have chosen a size of 100 for temp_name, but this should match the size of the string array in your actual struct. Note that the string conversion in sscanf() has a field width of 99, so that at most 99 non-space (not non-whitespace, and why aren't you just using %99s here?) characters are matched, leaving space for the '\0' to be added.
fgets() will return NULL when it reaches the end of the file, so the loop will continue until that happens. For each line that is read, a line counter is incremented. Then sscanf() is used to read data into the temporary variables. The value returned from sscanf() is checked to be sure that 3 assignments were made, and if so, then the data is copied into the struct, and idx is incremented. Note that strcpy() is needed to copy the string from temp_name to dota[idx].name.
If the value returned from sscanf() indicates that something other than 3 assignments were made, there is a check to see if buffer holds an empty line. If not, an error message is printed to stderr providing the line number of the bad input in the file.
A couple of further comments. Your do loop appears to be missing the associated while(). And you use fflush(stdin) after the scanf() inside the do loop. fflush()is meant to flush output streams. The behavior of fflush() on input streams is explicitly undefined in the C Standard (ISO/IEC 9899:2011 7.21.5.2/2), though I believe that Microsoft deviates from the Standard here. Nevertheless, you should not use fflush(stdin) in portable C code. Instead, use something like this:
int c;
...
scanf("%d", &choose);
while ((c = getchar()) != '\n' && c != EOF)
continue; // discard extra characters
This code reads characters from the input stream until either a '\n' or EOF is reached, clearing any characters left behind by the previous call to scanf() from the input stream.
I'm trying to learn C programming with some practical exercises.
This one for example should read 2 series of numbers (days of the month) input by the user.
The problem that I'm having is that after the first serie, that works ok, the program skips the other scanf commands and terminates.
Why?
#include <stdio.h>
#include <stdlib.h>
int main()
{
int f = 0;
int days1[31];
int days2[31];
printf("Insert first serie \n");
while(scanf("%d",&days1[f])) {
f++;
}
// the following scanf loop is skipped
f = 0;
printf("Insert second serie \n");
while(scanf("%d",&days2[f])) {
f++;
}
}
thanks
Read documentation of scanf. It returns the number of scanned items, and you should keep and test it. It could return -1 on failure (and that is seen as a true value) then don't consume any input. Its %n conversion specifier is often useful and not enough known.
So code
while(f < 31 && scanf("%d",&days1[f])>0) f++;
But you probably need to end the first series with something which makes scanf fail, and then you need to skip that something. You should define and document a convention about that. Let's pretend that you will type a semicolon ; then you'll code perhaps:
f=0;
while(f < 31 && scanf("%d",&days1[f])>0) f++;
if (getchar() != ';') exit(EXIT_FAILURE);
f=0;
while(f < 31 && scanf("%d",&days2[f])>0) f++;
I'm not sure the above work. You might need to care about spaces or empty lines. You could read each individual line with fgets or getline and parse each line (using sscanf or strtol).
Actually, I would recommend declaring a large enough buffer char buf[256];, use fgets to read a single line containing all the number series, then parse that line using strtol in a loop and caring about the end pointer given to strtol.
You need to document the input format, at least as comments. You could use EBNF notation for that, then use standard parsing techniques.
Of course you should compile with all warnings and debug info (e.g. gcc -Wall -Wextra -g if using GCC...). Then learn to use the debugger (e.g. gdb). It is a necessary skill to have.
I normally don't ask questions on here unless I'm really stuck! I was wondering if anyone can please explain why my code prints out a '5 47'. I understand why there is a 5, but not why there is a 47? I looked up the ASCII values for blankspace (32) and I tried changing the second letter to e, f, g, for example but the output remains '5 47' unchanged.
In general, when I use fscanf(fp, "%d", &variablename), does the fscanf skip over miscellaneous characters? For example: in my file test.txt I had "5 hello 6 ben jerry\n". How would I scan in the 5 and the 6? Would fscanf(fp, "%d %d", &test1, &test2) scan in the 5 and 6, skipping over the word "hello"?
Here is my simple code I am using to test output:
int main(int argc, char *argv[]) {
int blah, test;
FILE * fp;
fp = fopen(argv[1], "r");
fscanf(fp, "%d %d", &blah, &test);
printf("%d %d\n", blah, test);
return 0;
}
My file I am using as argv[1] contents:
5g
P.S. is FILE *fp an actual pointer to each character/number and does it work as a placeholder when it scans through the file? Is that why we need rewind(fp) once it hits the end of the file?
The operator %d looks for an integer, not a character. Because g is a character, not an integer, %d is getting confused and the output will not always be 5 47. The 47 could be anything. it could be 5 7, 5 23 etc. This is because the fscanf is not reading a second number, so no value is being assigned to test. Therefore, test remains at the value which was sitting in that piece of memory when the program was initiated.
To fix this, replace %d with %c and change the type of blah and test to int. Also, as WhozCraig said, it is good practise to check the return value of fscanf to check that two values have been found. This way, you can be sure that everything you're looking for has been found.
Note that the scanf() family of functions stop reading when they come across a character that is not expected by the format string. The unexpected character is left in the input for the next input operation to process.
If you want to read two integers that are definitely separated by a 'word' that is not an integer, then you will need to skip the word. If you don't know in advance what the word will be, you need to use assignment suppression (see the POSIX scanf() page for lots of information).
Hence, your code to read the two integers from input containing
5 hello 6 ben jerry
should be:
if (fscanf(fp, "%d %*s %d", &blah, &test) != 2)
…Oops; format error?…
Note that the code tests that it got the expected result. However, if you don't know whether there'll be a word between the two numbers, you are much better off using fgets() and sscanf() because you can try different parses of the same line:
char buffer[4096];
while (fgets(buffer, sizeof(buffer), fp) != 0)
{
if (sscanf(buffer, "%d %*s %d", &blah, &test) == 2)
…got two numbers with a word — let's go!
else if (sscanf(buffer, "%d %d", &blah, &test) == 2)
…got two numbers but no word — let's go!
else
…didn't recognize the format…
}
One of the major advantages of this is that you can report the error in terms of the complete line of input, rather than just the part that fscanf() didn't manage to work on.
Your last question, about a FILE *, is not a pointer to each character in the file. It is a handle which allows you to invoke functions that take a file pointer argument to read from or write to the associated file. However, you can't use indexing based on the file pointer (so fp[1024] does not identify the character at offset 1024 in the file or anything useful like that). If you want that sort of behaviour, you need a memory-mapped file (mmap() for POSIX systems).
I am trying to read in from a file, and I can't get the pattern of it right. Can someone tell me what I can do to get it working?
int main()
{
char name[20];
int age;
float highbp, lowbp, risk;
FILE *fp;
fp = fopen("data.dat", "r");
if(fp == NULL){
printf("cannot open file\n\n");
}
while(fscanf(fp, "name:%s\nage:%d\nbp:%f\nrisk:%f", name, &age, &highbp, &risk) != EOF){
}
printf("Name: %s\n", name);
printf("%d\n", age);
printf("%f\n", highbp);
printf("%f\n", risk);
}
data.dat:
name:tom
age:32
bp:43.00
risk:0.0
If it can't open the file it prints a message, but then continues. Instead it should return from main.
if (fp == NULL) {
printf("cannot open file\n\n");
return 1;
}
fscanf will return the number of items parsed, so it's probably safer to stop reading when the number returned < 4 (not all the items could be read).
Presumably "data.dat" contains multiple records and each line has a line ending. This means that after reading the first record the next character in the file is the line ending for the "risk:0.0" line. You should end the fscanf template with \n.
This is because the second time it tries to parse the file, fscanf will see that character, which it isn't expecting (the fscanf template starts "name:"), so it will stop reading, and you'll get only the first record.
You should change the "name" format specifier from %s to %19s to make it read at most 19 characters (+terminating '\0'). The way you have it now is a guaranteed failure in case someone gives you 20+ character name.
Can someone tell me what I can do to get it working?
I suggest you separate the functionality in different statements.
Don't try to cram all of the program functionality in 1 statement.
Your big statement is doing 3 things:
it is reading data from file
it is comparing the return value of scanf with EOF
it is controlling when to stop reading
I suggest you do (at least) 3 different statements for the 3 different actions.
Hint: comparing the return value of scanf only with EOF is a little too short
I am reading set of numbers from file by fscanf(), for each number I want to put it into array. Problem is that thoose numbers are separated by "," how to determine that fscanf should read several ciphers and when it find "," in file, it would save it as a whole number? Thanks
This could be a start:
#include <stdio.h>
int main() {
int i = 0;
FILE *fin = fopen("test.txt", "r");
while (fscanf(fin, "%i,", &i) > 0)
printf("%i\n", i);
fclose(fin);
return 0;
}
With this input file:
1,2,3,4,5,6,
7,8,9,10,11,12,13,
...the output is this:
1
2
3
4
5
6
7
8
9
10
11
12
13
What exactly do you want to do?
I'd probably use something like:
while (fscanf(file, "%d%*[, \t\n]", &numbers[i++]))
;
The %d converts a number, and the "%*[, \t\n]" reads (but does not assign) any consecutive run of separators -- which I've defined as commas, spaces, tabs, newlines, though that's fairly trivial to change to whatever you see fit.
fscanf(file, "%d,%d,%d,%d", &n1, &n2, &n3, &n4); but won't work if there are spaces between numbers. This answer shows how to do it (since there aren't library functions for this)
Jerry Coffin's answer is nice, though there are a couple of caveats to watch for:
fscanf returns a (negative) value at the end of the file, so the loop won't terminate properly.
i is incremented even when nothing was read, so it will end up pointing one past the end of the data.
Also, fscanf skips all whitespace (including \t and \n if you leave a space between format parameters.
I'd go for something like this.
int numbers[5000];
int i=0;
while (fscanf(file, "%d %*[,] ", &numbers[i])>0 && i<sizeof(numbers))
{
i++;
}
printf("%d numbers were read.\n", i);
Or if you want to enforce there being a comma between the numbers you can replace the format string with "%d , ".