How to Split strings with fgets or fscanf in C? - c

I understand how to read in a text file and scan/print the entire file, but how can a line be split into several strings? Also, can variables be assigned to those strings to be called later?
My code so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *fPointer;
fPointer = fopen("p1customer.txt", "r");
char singleLine[150];
int id;
while (!feof(fPointer)){
fscanf(fPointer, "%d", &id);
printf("%d",id);
}
fclose(fPointer);
return 0;
}
Example Text File to be read:
99999 John Doe Basketball
Example Output:
John Doe has ID number 99999 and plays Basketball
I am attempting to split/tokenize those strings and assign them variables (IDnumber, Name, Sport) and print the output in a new file.

you can use a library function strtok(str,chrs) function.
A sequence of calls of strtok(str,chrs) splits str into tokens, each delimited by a character from chrs.
The first call in a sequence is a non Null str.It finds the first token in str consisting of chars not int chrs;it terminates that by overwrtting the next characters of str by \0 and return pointer to token. Each subsequent call,indicated by a NULL value of str,retuens a pointer to next such token, searching from just past the end of privious one.

You should post an example of the input file so that you can help in more detail.
I've seen you've also entered a string, I guess you want to fill in with something but you did not specify that.
If you wanted to treat the file as a list of numbers, the sample of the code might be the following.
#include <stdio.h>
int main() {
FILE *infile;
char buf[100];
int len_file=0;
if(!(infile = fopen("p1customer.txt", "r"))) { /*checks the correct opening of the file*/
printf("Error in open p1customer.txt\n");
return 1;
}
while(fgets(buf,sizeof(buf),infile)!=NULL) /*check the lenght of the file (number of row) */
len_file++;
int id[len_file];
int i=0;
rewind(infile);
while(fgets(buf,sizeof(buf),infile)!=NULL) {
sscanf(buf,"%i",&id[i]);
i++;
}
for(i=0;i<len_file;i++)
printf("%i\n",id[i]);
fclose(infile);
return 0;
}
If you want to treat the file as an indefinite list of numbers on each row separated by a space, you can use the parsing of the string by using in the sscanf formatting %31[^ ]which has the task of reading the number until it encounters a space, also you can add a variable that is incremented for each char/number read.
Then you can refine the code by checking if there are any characters in the line using the isalpha function in the ctype.h library to see if there are any characters and then insert them into a string until you find the termination character '\ 0'.
The possibilities are infinite so it would useful have the input file, when you provided it, i'll update the answer.

Related

How to create 2D array from text file and print to new file in c

Wondering if I could get some advice. Firstly, I am very new to programming, so I apologise for any silly mistakes. Please feel free to point them out and I will try to go research to improve.
I feel I am doing something fundamentally wrong with my array.
I am trying to read in from a file whose filename is specified by user input, store the information from the file in a 2D array, which I then plan to print into another file, again defined by user input.
I am currently printing out the array, to check that it has been stored, but I believe I am using the 2D array incorrectly, as when I try to fprintf into my file, it just does not work.
Any advice would be greatly appreciated.
Thank you. Code as follows:
#include <stdio.h>
#include <string.h>
int main()
{
char finame[100];
printf("Enter file you would like to open: ");
scanf("%s", finame);
FILE *in = fopen(finame, "r");
char foname[100];
printf("Enter the filename you would like the output included in: ");
scanf("%s", foname);
FILE *out = fopen(foname, "w");
/*Char array to store string */
char str[50][20];
int i =0;
/*Loop for reading the file till end*/
while((fgets(str[i],sizeof(str[i]), in)) != NULL) {
fputs(str[i++],stdout);
//getchar();
}
return 0;
}
Avoid mixing fgets(), scanf() calls. scanf() leaves a newline character in the input buffer which is later consumed by fgets() (doesn't
matter in this case since input comes from a file not from stdin but a good practice overall).
There is also no protection for overflow, if you want to stick to scanf() add a width specifier and check the result to see if it succeeded.
if (scanf("%99s", finame) != 1) /* save one byte for \0 */
{
/* handle error case */
}
Check that you don't exceed the size of your array while writing to it.
Added 2 define directives that could clean your code up.
#define MAX_LINES 50
#define MAX_CHAR 20
char str[MAX_LINES][MAX_CHAR];
int i = 0;
while (i < MAX_LINES && (fgets(str[i], MAX_CHAR, in)) != NULL) /* always check if running out bounds */
{
fputs(str[i++], stdout);
}
The problem with the above code is that, if the file gets too big, you will end up missing data, what you could do is have a dynamic array and use a malloc / realloc approach to expand the array or a linked list.

Finding a string in a file using the lseek command in C

I have a file storing data of students in the following order:
id (space) name (space) address
Below is the content of the file:
10 john manchester
11 sam springfield
12 samuel glasgow
Each data is stored in a newline.
I want to search the student with id 10 and display his/her details using the lseek command, however I'm not to complete the task. Any help is appreciated.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void main() {
char line[50] = "";
char id[2] = "";
ssize_t fd = open("file.dat", O_RDONLY);
while(read(fd,line,sizeof(line))>0){
if (id[0] == '1' && id[1] == '0'){
printf("%s\n",line);
}
lseek(fd, 1 ,SEEK_CUR);
}
close(fd);
Use the right tools for the task. Hammer for nails, Screwdriver for screws.
lseek is not the right tool here, since lseek is for repositioning the file offset (which you do not have yet, you are looking for a specific position, when found, then you don't have a need for repositioning the file offset, since you are already there).
Ask yourself,
What is your task:
search for a specific id
print the line if match
What do you have:
a dataset (textfile) with a fixed format (id <space> name <space> address <newline>)
Your dataset is separated by a newline, and the id is the first field of that row.
The keywords here are 'newline' and 'first field'.
The right procedure here would be:
read a whole line (fgets)
compare the first field (start of line) with the desired id (strcmp)
Example:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main() {
//return value of main
int ret = EXIT_FAILURE;
//open filestream in read mode
FILE *f = fopen("file.dat", "r");
//string buffer
#define MAX_LEN 50
const char line[MAX_LEN];
char field[MAX_LEN];
//the id to search for
const char *id = "10";
//for each line
while (fgets(line, MAX_LEN, f)) {
//extract the first field ('%s' matches a sequence of non-white-space characters)
sscanf(line, "%s", field);
//compare the field with the desired id
if (strcmp(field, id) == 0) {
//if found print line
printf("%s", str);
//set result to success
ret = EXIT_SUCCESS;
//and exit
break;
}
}
//cleanup
fclose(f);
//return the result
return ret;
}
Your file has a first line that has 18 characters, a second line with the same number of characters, and a third one with one less (17) number of characters.
In case you have a four line in which the name for example makes the number of characters different, they should be appended to the file without any other structure.
Lines are delimited by \n characters, that can appear at any point, so second line starts as soon as just behind the first appearance of the \n char.
For this reason, you don't know the precise position where each line begins, and so you cannot know the exact position where each line begins, as the position of each line is (n + 1) bytes forward from twhere the previous line started, where n is the number of characters you put in the previous line, plus one (for the new line character).
You need an index, which is a file that allows you to get, on a fixed length record, to store the starting positions of each line in the data file. In this way, to read line i, you access the index at position (record_index_size * i), and get the position of the starting point of line i. Then you go to the data file, and position your file pointer to the value obtained from the las calculation, and read that with, for example fgets(3).
To build the index, you need to call ftell() right before each call to fgets(), because the call to fgets() will move the pointer, and so the position obtained will not be correct. Try to write the position in a fixed length format, e.g. binary form, with:
write(ix_fd, position, sizeof position);
so the position of line i, can be calculated by reading from index at position i * sizeof position.

How to store an array of strings in a file using C (one string per line)?

I want to store some strings in a text file (one string per line), but on compilation, some unnecessary zeroes get added at the end of each string in the text file.
Following is my code :-
**
#include <stdio.h>
#include <string.h>
int main()
{
int i=0;
char *name[4]={"ABC","agb","thi","yuun"};
FILE *pp;
pp=fopen("random_name.txt","w");
for(int i=0;i<4;i++)
{
fwrite(name[i],strlen(name[i])+1,1,pp);
fputs("\n",pp);
}
fclose(pp);
return 0;
}
**
Here's the text file
Just remove the +1 from strlen(name[i])+1 and update your fwrite() function:
fwrite(name[i],strlen(name[i]),1,pp);
unnecessary zeroes get added at the end of each string in the text
file.
Reason: Each of name[i] string ends with \0 (ASCII 0). You are giving, strlen(name[i])+1 as the argument to fwrite. So, the null terminating character (\0) will also be written to the file.
Solution : To avoid it, change fwrite(name[i],strlen(name[i])+1,1,pp); to fwrite(name[i],strlen(name[i]),1,pp);

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);

Correctly store content of file by line to array and later print the array content

I'm getting some issues with reading the content of my array. I'm not sure if I'm storing it correctly as my result for every line is '1304056712'.
#include <stdio.h>
#include <stdlib.h>
#define INPUT "Input1.dat"
int main(int argc, char **argv) {
int data_index, char_index;
int file_data[1000];
FILE *file;
int line[5];
file = fopen(INPUT, "r");
if(file) {
data_index = 0;
while(fgets(line, sizeof line, file) != NULL) {
//printf("%s", line); ////// the line seems to be ok here
file_data[data_index++] = line;
}
fclose(file);
}
int j;
for(j = 0; j < data_index; j++) {
printf("%i\n", file_data[j]); // when i display data here, i get '1304056712'
}
return 0;
}
I think you need to say something like
file_data[data_index++] = atoi(line);
From your results I assume the file is a plain-text file.
You cannot simply read the line from file (a string, an array of characters) into an array of integers, this will not work. When using pointers (as you do by passing line to fgets()) to write data, there will be no conversion done. Instead, you should read the line into an array of chars and then convert it to integers using either sscanf(), atoi() or some other function of your choice.
fgets reads newline terminated strings. If you're reading binary data, you need fread. If you're reading text, you should declare line as an array of char big enough for the longest line in the file.
Because file_data is an array of char, file_data[data_index] is a single character. It is being assigned a pointer (the base address of int line[5] buffer). If reading binary data, file_data should be an array of integers. If reading strings, it should be an array of string, ie char pointers, like char * file_data[1000]
you also need to initialize data_index=0 outside the if (file) ... block, because the output loop needs it to be set even if the file failed to open. And when looping and storing input, the loop should test that it's not reached the size of the array being stored into.

Resources