How to read comma delimited data from txt file to struct - c

I have text file which have the following content
inputfile
I have used a function to get data from the input file which is comma delimited.
I want to read data from it and want to remove comma and store the data to Struct Resistor_struct.
I have tried the following code.
'''
#include<stdio.h>
//functions header
int blown_ressistors();
struct resistor_struct
{
char ID_LEN[5];
char id;
float max_poewr;
int resistance;
};
struct resistor_struct rs[100];
int blown_ressistors()
{
FILE *fp = fopen("input.txt", "r");
int i = 0;
if(fp!=NULL)
{
while(fscanf(fp, "%s[^,], %d[^,], %f[^,]",rs[i].ID_LEN,rs[i].resistance, rs[i].max_poewr)!=EOF)
{
printf("%s\t", rs[i].ID_LEN);
printf("%d\t", rs[i].resistance);
printf("%d\t\n", rs[i].max_poewr);
i++;
}
}
else
{
perror("Input.txt: ");
}
'''
output
output image

You don't want to compare the value returned from scanf with EOF.
In your case, the format string is such that scanf can never match more than 1 conversion specifier, since %s[^,], is trying to match the literal input string [^,], but the [ is guaranteed not to match since the first character that scanf will stop consuming for the %s is whitespace. And [ is not whitespace. Try something like:
while(fscanf(fp, " %4[^,], %d, %f", rs[i].ID_LEN, &rs[i].resistance, &rs[i].max_poewr) == 3 )
but note that this will behave oddly on whitespace in the first column. You might want to try: " %4[^, \t\n] , %d, %f", but quite frankly the better solution is to stop using scanf. Even with something trivial like this, your behavior will be undefined on an input like foo, 9999...9999 (where the 2nd column is any value that exceeds the capacity of an int). Just stop using scanf. Read the data and parse it with strtol and strtod.

Related

Can I use a string in C and get two different data values from it?

Is it possible to take a string and get two different values from it to store in different type variables, more specifically, an integer and a character.
Brief example:
string : "A - 3"
int number : 3
char name : 'A'
I've seen people take multiple int from a string but nothing worked for me. I should add I am very new to C, so I don't really know how to mess around with it too much.
Its for a slide puzzle minigame where I need to read a text file with all instructions from the user, formatted with what direction each number block goes ("8 r" = 8 goes right), and print to the terminal each and every move. If anyone could suggest how I could take an int and char at the same time from a line on a file, I would also be grateful.
You can use fscanf(), defined in stdio.h, to read from a file:
int fscanf (FILE *stream, const char *format-string, argument-list)
stream is a file you previously opened. fscanf() returns the number of fields that it successfully converted and assigned. The return value does not include fields that the fscanf() function read but did not assign.
Example:
#include <stdio.h>
int main()
{
FILE* f = fopen("file.txt", "r");
int value;
char ch;
if (fscanf(f, "%d - %c", &value, &ch) != 2)
printf("Error");
printf("value: %d\nchar: %c\n", value, ch);
fclose(f);
return 0;
}
File file.txt content:
100 - A
Output:
value: 100
char: A
If your purpose is to parse a string, the simplest way to achieve this is to use strtok() defined in string.h header:
char *strtok(char *string1, const char *string2);

How to scan strings from a text file with delimiter and then display it?

So i tried to make a program that will scan strings from a text file and then display it using loop. But, somehow my program cannot work and it is display weird symbols.. i am new to text file and i would appreciate a lot if someone can explain to me what is wrong with my code.
My code :
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fPtr;
fPtr = fopen("alumni.txt", "r");
if (fPtr == NULL) {
printf("There is a error opening the file.");
exit(-1);
}
char name[20], design[50], category[20], location[20];
while (fscanf(fPtr, "%s:%[^\n]:%[^\n]:%[^\n]", &name, &design, &category, &location) != EOF) {
printf("Name : %s\n", name);
printf("Designation : %s\n", design);
printf("Category : %s\n", category);
printf("Location : %s\n", location);
}
}
and this is my text file,
Shanie:Programmer:Full Time:Kuala Lumpur
Andy:Sales Agent:Part Time:Johor Bahru
Elaine:Database Administrator Full Time Melaka
Stephanie:MIS manager:Full Time:Penang
You have two problems: The first is that %s will read space delimited "words", it won't stop at the :. The second problem is that the format %[^\n] reads all until newline.
So you need a scanset format for the first name as well as tell it to read until the next :, which is done with the format %[^:].
So please change to:
while (fscanf(fPtr, " %19[^:]:%49[^:]:%19[^:]:%19[^\n]", name, design, category, location) == 4) {
...
}
Please note a couple of other changes I made to your call and loop condition: First of all, I have added length specifiers to the formats, so fscanf will not write out of bounds of your arrays.
Secondly both the %s and %[] formats expects a char * argument, while you provided a pointer to arrays (&name will be of type char (*)[20] not char *). Arrays naturally decay to pointers to their first element, so e.g. name will decay to &name[0] which will be of the correct type char *.
Thirdly I changed the comparison to compare against 4, which is what fscanf will return if it successfully parsed the input.
Lastly I added a space before the first format, to skip any leading space (like the newline from the previous line).
To be sure to be able to continue even in the case of malformed input, I recommend you read full lines instead (using e.g. fgets), and then possibly use sscanf to parse each line.

Capture quoted strings separated with commas from a file

let's say I want to take an input from a file like this :-
"8313515769001870,GRKLK,03/2023,eatcp,btlzg"
"6144115684794523,ZEATL,10/2033,arnne,drrfd"
for a structure I made as follows
typedef struct{
char Card_Number[20];
char Bank_Code[6];
char Expiry_Date[8];
char First_Name[30];
char Last_Name[30];
}Card;
This is my attempt to read the input from a file named 'file' in the reading mode, the str in fgets is storing the right string but it isn't getting absorbed c[i]:
FILE * fptr;
int count=0;
fptr= fopen("file","r");
Card *c = (Card*)calloc(10,sizeof(Card));
printf("StartAlloc\n");
int i=0;
char str[1000];
fgets(str,80,fptr);
if(fptr==NULL)
{return 0;}
do{
sscanf(str,"\"%[^,],%[^,],%[^,],%[^,],%[^,]\" \n",c[i].Card_Number,c[i].Bank_Code,c[i].Expiry_Date,c[i].First_Name,c[i].Last_Name);
i++;
}while(fgets(str,80,fptr)!=NULL);
I do not understand why the regex %[^,] is not capturing the individual elements, I have wasted a lot of time, and help would be greatly appreciated.
The last token doesn't end with a ',', so you can't use %[^,] for it. It is however followed by a '\"', so you can use %[^\"] instead :
sscanf(str,"\"%[^,],%[^,],%[^,],%[^,],%[^\"]\" \n",c[i].Card_Number,c[i].Bank_Code,c[i].Expiry_Date,c[i].First_Name,c[i].Last_Name);
Using fscanf() with the proper format you can retrieve the desired elements from each line :
"\"%[^,]%*c %[^,]%*c %[^,]%*c %[^,]%*c %[^\"]%*c\n"
With the previous format, the opening quote is ignored (\"), and the strings separated by commas are captured (%[^,]%*c). Finally the the closing quote is discarded (%[^\"]%*c), and the line break considered (\n), to let next line to be read.
This is how you can integrate it in your code :
while (fscanf(file, "\"%[^,]%*c %[^,]%*c %[^,]%*c %[^,]%*c %[^\"]%*c\n", c[i].Card_Number, c[i].Bank_Code, c[i].Expiry_Date, c[i].First_Name, c[i].Last_Name) != -1 ) i++;
Complete code snippet for testing purposes :
#include <stdio.h>
#include <stdlib.h>
typedef struct{
char Card_Number[20];
char Bank_Code[6];
char Expiry_Date[8];
char First_Name[30];
char Last_Name[30];
}Card;
int main(){
FILE *file;
file = fopen("data.csv", "r");
int i=0;
Card *c = (Card*)calloc(10,sizeof(Card));
while (fscanf(file, "\"%[^,]%*c %[^,]%*c %[^,]%*c %[^,]%*c %[^\"]%*c\n", c[i].Card_Number, c[i].Bank_Code, c[i].Expiry_Date, c[i].First_Name, c[i].Last_Name) != -1 ) {
printf("%s | %s | %s | %s | %s \n", c[i].Card_Number, c[i].Bank_Code, c[i].Expiry_Date, c[i].First_Name, c[i].Last_Name);
i++;
}
fclose(file);
return 0;
}
If you just need to read from the file, you could just use fscanf() instead of reading from file to a character array and then use sscanf() for that string.
And you needn't explicitly type cast the return value of calloc(). See is it necessary to type-cast malloc and calloc.
You are doing
if(fptr==NULL)
{return 0;}
after you tried to read from the file. If the file couldn't be opened the program would crash well before the control reaches this if statement.
Place this check right after opening the file like
FILE *fptr = fopen("file", "r");
if(fptr==NULL)
{
return EXIT_FAILURE;
}
and return value 0 is usually taken to mean success. Since input file not being found is an error, try returning EXIT_FAILURE instead.
And in the last %[^,]" in the format string of sscanf function in your program, there is no comma for the last entry of each line in the input file. So change it to read till the last"` is found.
Also, at the end of the format string, there's a space followed by a \n. The \n is redundant here as a space will match "One white-space character in format-string matches any combination of white-space characters in the input"
So the final format string could be
"\"%[^,],%[^,],%[^,],%[^,],%[^\"]\" "
And don't forget to close the files you've opened and free the memory you've allocated before the end of the program like
free(c); //for the Card pointer
fclose(fptr);

Reading values from CSV file into variables

I am trying to write a simple piece of code to read values from a CSV file with a max of 100 entries into an array of structs.
Example of a line of the CSV file:
1,Mr,James,Quigley,Director,200000,0
I use the following code to read in the values, but when I print out the values they are incorrect
for(i = 0; i < 3; i++) /*just assuming number of entries here to demonstrate problem*/
{
fscanf(f, "%d,%s,%s,%s,%s,%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
}
Then when I print out the first name, the values are all assigned to the first name:
for(j = 0; j < 3; j++) /* test by printing values*/
{
printf("Employee name is %s\n", inArray[j].firstName);
}
Gives ames,Quigley,Director,200000,0 and so on in that way. I am sure it's how i format the fscanf line but I can't get it to work.
Here is my struct I'm reading into:
typedef struct Employee
{
int ID;
char salutation[4];
char firstName[21];
char surName[31];
char position[16];
int sal;
int deleted;
} Employee;
This is because a string %s can contain the comma, so it gets scanned into the first string. There's no "look-ahead" in the scanf() formatting specifier, the fact that the %s is followed by a comma in the format specification string means nothing.
Use character groups (search the manual for [).
const int got = fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID,
inArray[i].salutation, inArray[i].firstName,
inArray[i].surName, inArray[i].position, &inArray[i].sal,
&inArray[i].deleted);
And learn to check the return value, since I/O calls can fail! Don't depend on the data being valid unless got is 7.
To make your program read the entire file (multiple records, i.e. lines), I would recommend loading entire lines into a (large) fixed-size buffer with fgets(), then using sscanf() on that buffer to parse out the column values. That is much easier and will ensure that you really do scan separate lines, calling fscanf() in a loop will not, since to fscanf() a linefeed is just whitespace.
Might as well post my comment as an answer:
%s reads a full word by default.
It finds the %d, the integer part, then the ,, and then it has to read a string. , is considered valid in a word (it is not a whitespace), so it reads until the end of the line (there is no whitespace until then), not until the first comma... And the rest remains empty. (From this answer)
You have to change the separator with specifying a regex:
fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
Instead of %s, use %[^,], which means "grab all chars, and stop when found a ,".
EDIT
%[^,]s is bad, it would need a literal s after the end of the scanset... Thanks #MichaelPotter
(From Changing the scanf() delimiter and Reading values from CSV file into variables )

Program doesnt want to read file

this is my struct
typedef struct {
char mmsi[10];
char name[20];
double latitude;
double longitude;
int course;
double speed;
}Vessel;
this is my function which doesnt want to work
void searchByLatLong(double latitude, double longitude){
FILE * file;
struct dirent *drnt;
DIR * dir = opendir("./text");
char *path = (char *)malloc(19);
Vessel *vessel = (Vessel *)malloc(sizeof(Vessel));
while((drnt = readdir(dir)) != NULL) {
if(strcmp(drnt->d_name,".") && strcmp(drnt->d_name,"..")) {
strcpy(path,"text/");
strcat(path,drnt->d_name);
file=fopen(path, "r");
fscanf(file," %s %[a-zA-Z0-9 ]19s %lf %lf %d %lf", &vessel->mmsi,&vessel->name,&vessel->latitude,&vessel->longitude,&vessel->course,&vessel->speed);
// if (mmsi+".txt" == drnt->d_name){
printf("%s\n%s\n%lf\n%lf\n%d\n%lf\n\n",vessel->mmsi,vessel->name,vessel->latitude,vessel->longitude,vessel->course,vessel->speed);
//}
fclose(file);
}
seekdir(dir, telldir(dir));
// if(this->mmsi == mmsi){
// printVessel();
// }
}
closedir(dir);
}
When i try to load txt file it loads only two first strings then after it theres some rubbish from memory. Loading the data to another variables changes nothing ;/
This is a sample txt file which should be loaded:
3
RMS Titanic
22.222
33.333
4
5.9
The problem is with your format string. The correct format string is:
" %s %19[a-zA-Z0-9 ] %lf %lf %d %lf"
The field width goes before the conversion specifier. Also, the [...] sequence is a conversion specifier, just like 's'. The problem you're seeing is that fscanf() processes the '3' because it matches the first %s. Then it processes the 'RMS Titanic' because it matches %[a-zA-Z0-9 ] but then processing stops because there is no '19s' in the input. At this point the remaining arguments are uninitialized.
You should check the return value from fscanf(). It will tell you how many conversions were actually performed.
Thanks for posting an interesting question; I learned about fscanf() and the [] notation it accepts.
The [] notation specifies that a string is being read, therefore, the s you have appended to it is considered a literal character that should match. Similarly, the width-specifier you have provided, 19 should appear prior to the [].
Your current code would start working if you had a ship named, e.g., "RMS Titanic19s".
Change your fscanf to:
fscanf(file," %s %19[a-zA-Z0-9 ] %lf %lf %d %lf",
vessel->mmsi,vessel->name,&vessel->latitude,
&vessel->longitude,&vessel->course,&vessel->speed);
and your code will start working.
Notice I fixed some compile warnings by dropping the superfluous & from the char [] members mmsi and name -- these already point to the buffers you wish to fill. You don't need & in front of them. A pedagogical alternative form is &vessel->mmsi[0] -- the address of the first character of mmsi.

Resources