I have made a program which handles and stores records of movies in a library. This is how my library looks like when I print it out to a file.
So basicly my problem is to read this file and save those columns in my struct of variables and dynamically increase the size of the struct for every entry.
Right now I have a while loop reading line by line into a buffert and I am then trying to scan the values from the buffert.
I know the format of the spacings in between the columns if that helps.(No it's not tabs, would that help?)
Else i thought about once it finds "two spaces" it could parse onto the next one...
What I'm looking for is something like this:
("%s %d %d %d %s %d", name, &id, &qty, &price, genre, &year)
This is how the textfile looks like: "test.txt" (UPDATED version below)
-------------------------------------------------------------------------------
NAME ID QTY PRICE GENRE YEAR
-------------------------------------------------------------------------------
THE SHAWSHANK REDEMPTION 1 25 99 Crime 1994
THE GODFATHER 2 65 199 Crime 1972
PULP FICTION 3 265 99 Drama 1994
THE LORD OF THE RINGS 4 1024 199 Action 2003
THE DARK KNIGHT 5 99 99 Action 2008
And this is how my code looks like:
void read_from_file(struct movies *movie)
{
FILE *fp;
char buffer[1024];
int line_num = 0;
int index = 0;
fp = fopen("test.txt", "r");
if ( fp == NULL )
{
printf("Error opening file!\n");
exit(1);
}
fgets(buffer, 1024, fp); fgets(buffer, 1024, fp); fgets(buffer, 1024, fp); // Skip 3 first lines (header)
while(fgets(buffer, 1024, fp) != NULL) // read one line to buffer at a time
{
printf("%s", buffer); //DEBUG
// Trying here to read only the names which i know should be no longer than 40chars. first row goes well, then it's messed up
sscanf(buffer, "%40[^\0]" , movie[index].name);
index++;
}
fclose(fp);
getch();
}
Update: I changed the format of the storage file to the following:
THE SHAWSHANK REDEMPTION,1,25,99,Crime,1994
THE GODFATHER,2,75,199,Crime,1972
PULP FICTION,3,512,99,Drama,2000
THE LORD OF THE RINGS,4,1024,199,Action,2003
THE DARK KNIGHT,5,99,99,Action,2008
The main problem is that there is no single char delimiter. So process the first 45 char;
while(fgets(buffer, sizeof buffer, fp) != NULL) {
printf("%s", buffer); //DEBUG
if (strlen(buf < 80)) {
handle_short_line_error();
}
size_t len;
for (len = 45; len > 0; len--) {
if (!isspace(buf[len-1])) {
break;
}
}
if (!IsAGoodLength(len)) {
handle_format_error();
}
memcpy(movie[index].name, buf, len);
movie[index].name[len] = '\0';
if (5 != sscanf(&buffer[45], "%d%d%d%9s%d", &id, &qty, &price, genre, &year)) {
handle_format_error();
}
// Use other fields scanned
index++;
}
Related
I have this code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* ptr = fopen("data.txt","r");
char filename[100];
if (ptr==NULL)
{
printf("no such file.");
return 0;
}
char buf[100];
while (fscanf(ptr,"%*s %*s %s ",buf)==1)
printf("%s\n", buf);
printf("Create a file \n");
scanf("%s", filename);
fptr2 = fopen(filename, "w");
if (fptr2 == NULL)
{
printf("Cannot open file %s \n", filename);
exit(0);
}
c = fgetc(fptr1);
while (c != EOF)
{
fputc(c, fptr2);
c = fgetc(fptr1);
}
printf("\nContents copied to %s", filename);
fclose(fptr1);
fclose(fptr2);
return 0;
}
}
It coppies full content from one file to another. I need to copy only strings that have 5 as the last character (3 column)
For example Data.txt looks like that:
Alex 10B 4
John 10A 3
Kate 10C 5
In file that I will create during execution has to be coppied only Kate 10C 5 string. I've been trying for hours but I don't know how to do this. Can you help me?
In the end of each line there is a newline character, (\n) you can use that to read line by line and copy only the ones that you want:
FILE* dest = fopen("out.txt", "w+"); // supressed null check for simplicity
char buf[100];
char* char_to_find;
// parse line by line
while (fscanf(ptr, " %99[^\n]", buf) == 1){
char_to_find = buf;
// reach the end of the line
while(*char_to_find){
char_to_find++;
}
//move one back
char_to_find--;
// if it's 5 save, if not move on
if(*char_to_find == '5' && *(char_to_find - 1) == ' '){
fputs(buf, dest);
}
}
Live demo
The problem is that the function call
while (fscanf(ptr,"%*s %*s %s ",buf)==1)
consumes the input from the input stream, so that it is no longer available for copying. You are only saving the contents of the last field, but all other data is lost.
I suggest that you read one line at a time into a memory buffer, by calling the function fgets in a loop. That way, you will process one line of input per loop iteration, and will be saving the contents of the entire line.
In every loop iteration, you can use sscanf on this memory buffer to determine whether the third field has the desired value, and if it does, then you copy the entire line to the output file. Otherwise, you do nothing and proceed to the next line (i.e. the next loop iteration).
char line[100];
//process one line of input per loop iteration
while ( fgets( line, sizeof line, input_file ) != NULL )
{
char third_field[20];
if (
//third field was successfully extracted
sscanf( line, "%*s%*s%19s", third_field ) == 1
&&
//third field contains the string "5"
strcmp( third_field, "5" ) == 0
)
{
//copy entire line to output file
fputs( line, output_file );
}
}
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* ptr = fopen("data.txt","r");
char filename[100];
if (ptr==NULL)
{
printf("no such file.");
return 0;
}
printf("Create a file \n");
scanf("%s", filename);
FILE* dest = fopen(filename, "w+"); // check for null like above
char buf[100];
char* char_to_find;
while (fscanf(ptr,"%99[^\n] ", buf) == 1){
char_to_find = buf;
while(*char_to_find != 0){
char_to_find++;
}
char_to_find--;
if(*char_to_find == '5'){
printf("%s\n", buf); // test ptint
fputs(buf, dest);
}
}
}
I'm working on a project of booking , so the idea of project when the program starts it should read a data from a file called databook and save them on the struct , and everytime i'm adding a book it should add to program
the difficult i found the name has a space between name and nickname so i used scanf but the problem is scanf don't read full line i used scanf("%[^\n]s",) but it doesnt work
the source code below to understund more
#include <stdio.h>
#include <stdlib.h>
#define MAX_ARRAY_SIZE 5
typedef struct Book
{
char BookName[50];
int BookISBN;
int Borrowed;
char BorrowerName[50];
char Field[50];
}Book;
Book book[MAX_ARRAY_SIZE];
void ReadFile(char* fileName);
int main(int argc, char* argv[])
{
char* fileName = "c1.txt";
ReadFile(fileName);
int i = 0;
for (i = 0; i < MAX_ARRAY_SIZE; i++)
{
printf("Book Name is : %s\n", book[i].BookName);
printf("Book ISBN is : %d\n", book[i].BookISBN);
printf("Borrowed is : %d\n", book[i].Borrowed);
printf("Borrower Name is : %s\n", book[i].BorrowerName);
printf("Field is : %s\n", book[i].Field);
printf("\n");
}
exit(EXIT_SUCCESS);
}
void ReadFile(char* fileName)
{
FILE* filePtr = NULL;
int i = 0;
if ((filePtr = fopen(fileName, "r")) == NULL)
{
printf("Error : Unable to open %s for reading\n");
exit(EXIT_FAILURE);
}
while (fscanf(filePtr, "%s%d%d%s%s", &book[i].BookName, &book[i].BookISBN, &book[i].Borrowed,&book[i].BorrowerName,&book[i].Field) != EOF)
{
i++;
}
fclose(filePtr);
}
for databook
Technique Informatique //BookName1
90023 //BookISBN1
1 //(OR O) - means 'Borrowed OR not
Adam Ridge //BorrowerName1 (None in case Not borrowed)
special//(field)
Data Structures //BookName1
23451 //BookISBN1
0 //(OR O) - means 'Borrowed OR not
None //BorrowerName1 (None in case Not borrowed)
Computer Science //(field)
E-commerce Blockchain //BookName1
14678 //BookISBN1
1 //(OR O) - means 'Borrowed OR not
Adam Ridge //BorrowerName1 (None in case Not borrowed)
Business //(field)
Well you should be using fgets for reading string, as other people have already said.
Nevertheless, and to answer you question, here is some advice that might help you:
Book structure contains char arrays (char []) not pointers (char *), so reading into those variables does nor have the & symbol, e.g.:
fscanf(filePtr, "%[^\n]%d\n%d\n%[^\n]%*c%[^\n]%*c", book[i].BookName...) != EOF)
The correct format of the fscanf function for this case is:
fscanf(filePtr, "%[^\n]%d\n%d\n%[^\n]%*c%[^\n]%*c", book[i].BookName, &book[i].BookISBN, &book[i].Borrowed, book[i].BorrowerName, book[i].Field) != EOF)
Where:
a) %[^\n] -> reads a full line
b) %d\n -> reads an int and an end of line
c) [^\n]%*c -> reads a full line and any extra chars. Note, that if you use * in the format string, it suppresses assignment:
%*c = read 1 character, but don't assign it to any variable
As already pointed out by people in comments, you should try to use fgets() for reading file and then sscanf for reading integers from the string. Here is an example:
int ReadFile(char* fileName) {
FILE* filePtr = NULL;
int i = 0;
if ((filePtr = fopen(fileName, "r")) == NULL) {
printf("Error : Unable to open %s for reading\n");
exit(EXIT_FAILURE);
}
char input[100];
char *result;
while ((result = fgets(input, 100, filePtr)) != NULL) {
// Book Name
input[strlen(input) - 1] = '\0'; // Trim trailing \n
strcpy(book[i].BookName, input);
// Book ISBN
result = fgets(input, 100, filePtr);
if (result == NULL) break;
sscanf(input, "%d", &book[i].BookISBN);
// Book Borrowed
result = fgets(input, 100, filePtr);
if (result == NULL) break;
sscanf(input, "%d", &book[i].Borrowed);
// Book Borrower Name
result = fgets(input, 100, filePtr);
input[strlen(input) - 1] = '\0'; // Trim trailing \n
if (result == NULL) break;
strcpy(book[i].BorrowerName, input);
// Book Field
result = fgets(input, 100, filePtr);
input[strlen(input) - 1] = '\0'; // Trim trailing \n
if (result == NULL) break;
strcpy(book[i].Field, input);
i++;
}
fclose(filePtr);
return i;
}
When I run this code on your file, I'm able to see documents being printed:
c-posts : $ gcc booking.c
c-posts : $ ./a.out
Book Name is : Technique Informatique
Book ISBN is : 90023
Borrowed is : 1
Borrower Name is : Adam Ridge
Field is : special
Book Name is : Data Structures
Book ISBN is : 23451
Borrowed is : 0
Borrower Name is : None
Field is : Computer Science
Book Name is : E-commerce Blockchain
Book ISBN is : 14678
Borrowed is : 1
Borrower Name is : Adam Ridge
Field is : Business
I got this code from a question and i don't understand why they have picked exactly 300 for line[300] is it because a line of .txt files have exactly 300 characters.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *cfPtr = fopen("clients.txt", "r");
if(cfPtr == NULL)
{
puts("The file can't be open");
return 0;
}
char name[11], sex[11], dad[11], mom[11], line[300];
int age;
fgets(line, sizeof(line), cfPtr); //skip the first line
while(fgets(line, sizeof(line), cfPtr))
{
if(5 == sscanf(line, "%10s%10s%10d%10s%10s", name, sex, &age, dad, mom))
printf("%s, %s, %d, %s, %s\n", name, sex, age, dad, mom);
}
fclose(cfPtr);
return 0;
}
There is no such rule or restriction for .txt files.
As for taking 300 as the size of the line array and using it in fgets(line, sizeof(line), cfPtr);, it is only stating that fgets will take atmost 300 character inputs from a line (it can take less if there are less). Most probably, it is taken as an assumption that no line will have more than 300 characters so this is just an upper limit.
fgets(line, sizeof(line), cfPtr)
fgets reads in at most one less than sizeof(line) (300 bytes) from stream cfPtr and stores them into line.
The example below maybe help you:
#include <stdio.h>
int main()
{
FILE *fp = fopen("input.txt", "r");
char line[4]; // string length ups to 3 with this declaration.
while(fgets(line, sizeof(line), fp)) {
printf("%s\n",line);
}
printf("enter the string: \n");
fgets(line, sizeof(line), stdin);
printf("string from keyboard %s\n", line);
fclose (fp);
return 0;
}
The input file and output:
$cat input.txt
123456789
./test
123 // print 3 characters not 10 (one for enter character) characters of the line
456
789
enter the string:
1234567
line from keyboard 123
You can see the result. Even you type 1234567 from the keyboard, the string you got is 123 instead of 1234567.
This is one reason makes fgets is better and safer than gets when you enter string from stdin.
I am currently learning reading from files in C.
Anyway, cutting to the chase:
Text file content:
123456 James Doakes; 0
987987 Dexter Morgan; 0
010203 Masuka Perv; 0
int main()
{
char accountNr[ACCOUNTNRSIZE], ownerName[NAMESIZE], enter[3];
int accountBalance = 0;
char filename[] = "breg.txt";
FILE *file = fopen(filename, "r");
if (file != NULL) {
char line[128];
while (fgets(line, sizeof(line), file) != NULL) {
sscanf(line, "%s %[^;] %d ", accountNr, ownerName, &accountBalance);
printf("%s", ownerName);
//fflushstdin();
}
fclose(file);
} else {
perror(filename);
}
return 0;
}
I wrote this to check if the name for instance James Doakes was registered correctly :
printf("%s", ownerName);
But when it prints that out it's like the stdout is still active and I can push Enter and it will type the name again. My goal is to of course be able to sscanff the number, the full name, and the last number as seperate variables. But it obviously doesn't work. I am guessing a \n gets registered as well. Dunno, I am just speculating.
What am I doing wrong? Why? And how do I solve this?
Much appreciated,
Mif
%s %[^;] %d
means a string terminated by white space, optional white space, a sequence of characters that are not ;, optional white space, then a number.
You appear to be not scanning for the actual ; character itself so that, when you try to get the number, the ; in the input stream will cause it to fail. You can see this with:
#include <stdio.h>
#define ACCOUNTNRSIZE 100
#define NAMESIZE 100
int main (void) {
char accountNr[ACCOUNTNRSIZE], ownerName[NAMESIZE], enter[3];
int accountBalance = 0;
char filename[] = "breg.txt";
FILE *file = fopen(filename, "r");
if (file != NULL) {
char line[128];
while (fgets(line, sizeof(line), file) != NULL) {
int count = sscanf(line, "%s %[^;] %d ", accountNr, ownerName, &accountBalance);
printf ("%d [%s] [%s] [%d]\n", count, accountNr, ownerName, accountBalance);
}
fclose(file);
} else {
perror(filename);
}
return 0;
}
which outputs:
2 [123456] [James Doakes] [0]
2 [987987] [Dexter Morgan] [0]
2 [010203] [Masuka Perv] [0]
In fact, even if you change the breg.txt file to be:
123456 James Doakes; 314159
987987 Dexter Morgan; 271828
010203 Masuka Perv; 42
you still get 0 for the account balance because the scanning only successfully reads two items.
Whenever you use one of the scanf-family functions, you should check the return code to ensure it's scanning the correct number of items, as in:
int count = sscanf (line, "%s %[^;] %d ", accountNr, ownerName, &accountBalance);
if (count != 3) {
fprintf (stderr, "Catostrophic failure, count is %d\n", count);
return 1;
}
The fix here is relatively simple, just use %s %[^;]; %d as the format string.
With that change, the output you see is:
3 [123456] [James Doakes] [314159]
3 [987987] [Dexter Morgan] [271828]
3 [010203] [Masuka Perv] [42]
Keep in mind you don't actually need a space before the %d (though it causes no harm). That particular format specifier skips white space before attempting to scan the number.
char string[50], s[50];
File *f = tmpfile();
count = 1;
while (fgets(string, 50, stdin)) {
if (string[0] == '!') {
rewind(f);
} else {
fwrite(string, 50, 1, f);
}
if (strcmp("history\n", string) == 0) {
rewind(f);
while(fgets(s, 50, f)) {
printf("\t%d %s", count, s);
count++;
}
count = 1;
}
}
The context of this code is not hugely important. The problem is that let's say fgets takes in "ls", "date", and "history". The resulting output is:
1 ls
2 3 te
4 5 ory
6
It should be:
1 ls
2 date
3 history
Since feature requests to mark a comment as an answer remain declined, I copy the above solution here.
Looks like you get some '\r's in your buffer. And you should probably only fwrite strlen(string) bytes. – Daniel Fischer