Reading columns of strings and integers from file - c

I am able to read chars, words, sentences and integers from separate files but I am struggling to read words and integers from the same file. Let's say my file contains the following:
Patrice 95
Rio 96
Marcus 78
Wayne 69
Alex 67
Chris 100
Nemanja 78
My partial solution (to read in strings) so far was to use fgetc() and check for spaces and or carriage returns in my text file to separate the name from the number.
The main issue with fgetc is that it reads in character by character, and so integers are not meant to be read in like this. As a workaround, I am converting the character to an integer whenever a number is read in.
This is the main code structure:
typedef struct person {
char name[10][10];
char surname[10][10];
int age [10];
} person_t;
FILE *inp; /* pointer to input file */
char c;
int word_count = 0;
int char_count = 0;
int i = 0;
int x;
person_t my_person;
while ((c = fgetc(inp)) != EOF) {
if (c == ' ' || c == '\r') {
printf("\n");
my_person.name[word_count][char_count] = '\0'; //Terminate the string
char_count = 0; //Reset the counter.
word_count++;
}
else {
if (c >= '0' && c <= '9') {
x = c - '0'; //converting to int
my_person.age[i] = x;
printf("%d", my_person.age[i]);
i++;
}
else {
my_person.name[word_count][char_count] = c;
printf("%c",my_person.name[word_count][char_count]);
if (char_count < 19) {
char_count++;
}
else {
char_count = 0;
}
}
}
}
}
for (int i = 0; i<7; i++) {
printf("ages: %d \n",my_person.age[i] ); //never executes
}
Sample Output:
Patrice
95
Rio
96
Marcus
78
Wayne
69
Alex
67
Chris
Full code can be found on pastebin.
Why is the for loop never executing? Any suggestions on what I can improve to read the columns of strings and integers?

Use fgets() to read a whole line.
char line[100];
while (fgets(line, sizeof line, inp)) {
// got a line, need to isolate parts
}
Then, depending on whether the words can have embedded spaces choose one of the strategies below.
a) sscanf() to isolate name and age
while (fgets(line, sizeof line, inp)) {
char name[30];
int age;
if (sscanf(line, "%29s%d", name, &age) != 2) /* error, bad line */;
// ...
}
b) strrchr() to find the last space, then string manipulation to extract name and age.
while (fgets(line, sizeof line, inp)) {
char name[30];
int age;
char *space = strrchr(line, ' ');
if (!space) /* error, bad line */;
if (space - line >= 30) /* error, name too long */;
sprintf(name, "%.*s", space - line, line);
age = strtol(space, NULL, 10); // needs error checking
// ...
}
strategy b) on https://ideone.com/ZOLie9

Related

C Reading a file of digits separated by commas

I am trying to read in a file that contains digits operated by commas and store them in an array without the commas present.
For example: processes.txt contains
0,1,3
1,0,5
2,9,8
3,10,6
And an array called numbers should look like:
0 1 3 1 0 5 2 9 8 3 10 6
The code I had so far is:
FILE *fp1;
char c; //declaration of characters
fp1=fopen(argv[1],"r"); //opening the file
int list[300];
c=fgetc(fp1); //taking character from fp1 pointer or file
int i=0,number,num=0;
while(c!=EOF){ //iterate until end of file
if (isdigit(c)){ //if it is digit
sscanf(&c,"%d",&number); //changing character to number (c)
num=(num*10)+number;
}
else if (c==',' || c=='\n') { //if it is new line or ,then it will store the number in list
list[i]=num;
num=0;
i++;
}
c=fgetc(fp1);
}
But this is having problems if it is a double digit. Does anyone have a better solution? Thank you!
For the data shown with no space before the commas, you could simply use:
while (fscanf(fp1, "%d,", &num) == 1 && i < 300)
list[i++] = num;
This will read the comma after the number if there is one, silently ignoring when there isn't one. If there might be white space before the commas in the data, add a blank before the comma in the format string. The test on i prevents you writing outside the bounds of the list array. The ++ operator comes into its own here.
First, fgetc returns an int, so c needs to be an int.
Other than that, I would use a slightly different approach. I admit that it is slightly overcomplicated. However, this approach may be usable if you have several different types of fields that requires different actions, like a parser. For your specific problem, I recommend Johathan Leffler's answer.
int c=fgetc(f);
while(c!=EOF && i<300) {
if(isdigit(c)) {
fseek(f, -1, SEEK_CUR);
if(fscanf(f, "%d", &list[i++]) != 1) {
// Handle error
}
}
c=fgetc(f);
}
Here I don't care about commas and newlines. I take ANYTHING other than a digit as a separator. What I do is basically this:
read next byte
if byte is digit:
back one byte in the file
read number, irregardless of length
else continue
The added condition i<300 is for security reasons. If you really want to check that nothing else than commas and newlines (I did not get the impression that you found that important) you could easily just add an else if (c == ... to handle the error.
Note that you should always check the return value for functions like sscanf, fscanf, scanf etc. Actually, you should also do that for fseek. In this situation it's not as important since this code is very unlikely to fail for that reason, so I left it out for readability. But in production code you SHOULD check it.
My solution is to read the whole line first and then parse it with strtok_r with comma as a delimiter. If you want portable code you should use strtok instead.
A naive implementation of readline would be something like this:
static char *readline(FILE *file)
{
char *line = malloc(sizeof(char));
int index = 0;
int c = fgetc(file);
if (c == EOF) {
free(line);
return NULL;
}
while (c != EOF && c != '\n') {
line[index++] = c;
char *l = realloc(line, (index + 1) * sizeof(char));
if (l == NULL) {
free(line);
return NULL;
}
line = l;
c = fgetc(file);
}
line[index] = '\0';
return line;
}
Then you just need to parse the whole line with strtok_r, so you would end with something like this:
int main(int argc, char **argv)
{
FILE *file = fopen(argv[1], "re");
int list[300];
if (file == NULL) {
return 1;
}
char *line;
int numc = 0;
while((line = readline(file)) != NULL) {
char *saveptr;
// Get the first token
char *tok = strtok_r(line, ",", &saveptr);
// Now start parsing the whole line
while (tok != NULL) {
// Convert the token to a long if possible
long num = strtol(tok, NULL, 0);
if (errno != 0) {
// Handle no value conversion
// ...
// ...
}
list[numc++] = (int) num;
// Get next token
tok = strtok_r(NULL, ",", &saveptr);
}
free(line);
}
fclose(file);
return 0;
}
And for printing the whole list just use a for loop:
for (int i = 0; i < numc; i++) {
printf("%d ", list[i]);
}
printf("\n");

Splitting and modifying a string from text file in C

In a school project I am making a Caesar's Cipher. The requirements I have to meet are the following: I need to read text from a text file, store it into a bi-dimensional array of strings with a maximum of 81 characters per line(80 useful + '\0') and 1000 lines, and then modify the content in order to cipher or decipher it. What if in a single line of text from the file I has more than 80 useful characters? I thought about making it read in a way that each space it reads transforms it into a '\0' and changes line in the array but I don't know if I can do it with fgets, instead of fgetc as I was doing it.
This is what I have right now:
int lerficheiro(char * texto[MAXLINHAS][MAXCARPORLINHA])
{
char caractere;
FILE * fp;
fp = fopen("tudomaiusculas.txt", "r");
if(fp==NULL)
{
printf("Erro ao ler ficheiro.");
return (-1);
}
for(int linha = 0; linha < MAXLINHAS; linha++)
{
for(int coluna = 0; coluna < MAXCARPORLINHA; coluna++)
{
caractere = fgetc(fp);
if(caractere == ' ') caractere = '\0'; break;
if(caractere == '\n') caractere = '\0'; break;
if(caractere < 'A' || caractere > 'Z')
{
printf("Erro ao ler, o ficheiro não contem as letras todas
maiusculas");
return (-1);
}
* texto[linha][coluna] = caractere;
}
}
}
Initialize your array to 0 and make use of the fact that you select how many bytes you are reading using fgets. Just check the return value of fgets to see if you've reached the end of the file.
Also note that you should not need to have your result array as a pointer, since array already are pointers (EDIT to mirror #user3629249 suggestions)
EDIT2: Edited code to take into account the new-line problem. Alo removed the -1 that was causing 79 char lines instead of 80
#include <stdio.h>
#include <string.h>
#define MAX_LINES 8000
#define HSIZE 81
int parse_file( FILE * inp_file, char res[MAX_LINES][HSIZE])
{
int l = 0;
int len = HSIZE;
while( fgets( res[l]+(HSIZE-len), len, inp_file ))
{
len = HSIZE - strlen( res[l]);
if( len <= 1)
{
l++;
len = HSIZE;
}
}
}
int main()
{
char parsed_file[MAX_LINES][HSIZE] = {0};
FILE * inp_file;
inp_file = fopen( "file_to_parse.txt", "r");
if( inp_file == NULL)
{
printf( "Failed to read input file...\n");
return 1;
}
parse_file( inp_file, parsed_file);
fclose( inp_file);
for( int i=0; parsed_file[i][0] != 0; i++)
printf( "line %04d: %s\n", i+1,parsed_file[i]);
return 0;
}
If you want you could also replace the new line in your parsed_file with something like this
char *pos;
while( (pos = strchr( line, '\n'))
*pos = ' ';
With test file:
This is a random file that I'm testing out for the pure randomness of random files.
Still reading, m'kay man lets get going!!!!!!!!!!! So last day the craziest thing happened, let me tell you about it....
and output
line 0001: This is a random file that I'm testing out for the pure randomness of random fil
line 0002: es.
Still reading, m'kay man lets get going!!!!!!!!!!! So last day the craziest
line 0003: thing happened, let me tell you about it....
Take care that the printf will still print the newline character

Distinguish between new line and errors when using sscanf

I'm having a harsh time achieving this. I read a string of integer, extract them from that chain and put them in an array. I wanted to try something new because a used to read character by character with getChar in the past, but now I find sscanf which handle the job.
From Doc (sscanf) :
On success, the function returns the number of items in the argument list successfully filled. This count can match the expected number of items or be less (even zero) in the case of a matching failure.
In the case of an input failure before any data could be successfully interpreted, EOF is returned.
line : char line[] = "100 185 20 11 1000"; // number of int is unknown, it can differ
Problem: How to make diffrence between end of line and a whatever error
Eg: char line[] = "100 185 abc(not int) 11 1000";
Code :
int main(int argc, char** argv) {
char line[] = "100 185 20 11 1000";
int arrOfInt[10];
char *data = line;
int track, number, index = 0;
while ((sscanf(data, " %d%n", &number, &track)) == 1)
{
arrOfInt[index] = number;
data += track;
index++;
}
return 0;
}
#BLUEPIXY mentioned the correct answer. Just formalizing it here.
If the return value of scanf is EOF that means an EOF is seen. If it is 0 that means some other error has occurred and the value set via %n is undefined(eg. the input does not match the format specified).
The following approach works as long as there are no tokens with mixed alphabets and letter like "hey45hey2".
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
char line[] = "100 185 20 11 1000 sdf 342 hey45hey2";
int arrOfInt[10];
char *data = line;
int track, status = 0, number, index = 0;
// read until an EOF is found
while ((status = sscanf(data, " %d%n", &number, &track)) != EOF)
{
// store only if status is non-zero i.e. sscanf has read according to the format specified
if(status != 0){
arrOfInt[index] = number;
index++;
}
else
track=1;
data += track;
}
// print the array back to check if we read correctly
int i;
for(i=0;i<index;i++)
printf("%d ", arrOfInt[i]);
return 0;
}
The output of this code is:
100 185 20 11 1000 342 45 2
If you want to exclude strings like "hey45hey2" from the output, then use the following.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
char line[] = " egsdfgfd rgege 100 185 20 11 hey45hey2 0d0 00 sddbrsdtbrtsdbrsf 342";
int arrOfInt[10];
char *data = line;
int track, status = 0, index = 0;
char str[100]; // will fail for tokens of length more than 100
// read until an EOF is found
while ((status = sscanf(data, "%s%n", str, &track)) != EOF)
{
// store only if status is non-zero
if(status != 0){
// check if str is all zeros
int num=(int)strtol(str,0,10);
char *i = str;
if(num == 0){
while(*i){
if(*i != '0') break;
i++;
}
}
if(*i == 0 || num != 0){
// if *i is zero, then num is also zero
arrOfInt[index] = num;
index++;
}
}
else
track = 1;
data += track;
}
// print the array back to check if we read correctly
int i;
for(i=0;i<index;i++)
printf("%d ", arrOfInt[i]);
return 0;
}
Output is 100 185 20 11 0 342
How to make diffrence between end of line and a whatever error
Sorry to say this, but the answer is: Drop sscanf, and do the parsing yourself.
If, for what ever reasons, you need/want to stick to sscanf you could always do a test on the "string" after sscanf is done, to see if there is more after the last token read, as you know the number of tokens parsed. You would do this for example by tokenizing the string beforehand using strtok on whitespaces, if you scanned in few, then you'd advance the string pointer behind the last+1 token and continue using sscanf. But is this sane? ;-)
I achieved what I really wanted to do by using your advice. Just to mention that when I find undesired character I have to leave my loop and show error.
Code(can be useful for others)
int main(int argc, char** argv) {
char line[] = "100 185 20 11 1000"; // or "100 185 abc 11 1000"
int arrOfInt[10];
char *data = line;
int track, number, index = 0;
int statutLine;
while ((statutLine = sscanf(data, " %d%n", &number, &track)) == 1)
{
arrOfInt[index] = number;
data += track;
index++;
}
if(statutLine == 0){
printf("Line invalide");
}
if(statutLine == EOF){
printf("Line valide");
}
return 0;
}

Reading file and storing into variable

I have spent the last 4 or 5 hours on something that is probably quite simple, I have tried many different methods. What I am trying to do is make my program save the end of each line in a saved test file. This would be the players names followed by the shirt number or weight etc. The file is saved in a format like this:
First name is:xxxxxx
Last name is:xxxxx
Date of birth is:xxxxxx
I want my code to be able to just store the xxxxxx in a separate variable so then i can un-encrypt it as only them parts are encrypted.
The code I have got so far is
int main()
{
int dob;
char lastname[15], *ptr, filename[25], line[40], storage[200], a[15];
FILE *file;
printf("Please enter the last name of your player: ");
scanf("%s", lastname);
printf("\nPlease enter the date of birth of your player: ");
scanf("%d", &dob);
printf("\n\n");
sprintf(filename, "%s%6d.pat", lastname, dob);
printf("%s", filename);
file = fopen(filename, "r");
while(fgets(line, sizeof(line), file) != NULL)
{
fprintf(stdout, "%s", line);
}
fscanf(file, "%s: %s\n", storage, a);
printf("%s %s", storage, a);
I am now currently trying to use this piece of code to the get the last string after the : then apply a small decryption to it, but i seem to get an infinite loop of just the name after the first line.
do
{
if(sscanf(line,"%*[^:]:%19s",s)==1)
{
for(i = 0; i < slen; i++) /*encrypting firstname*/
{
slen = strlen(s);
s[i] = (char)((s[i] - 'a' - 4) % 26 + 'a');
if(s == '\0')
{
break;
}
}
printf("%s",s);
slen = strlen(s);
slen--;
}
}while(slen > 0);
You can make use of sscanf function -
char s[20];
if(sscanf(line,"%*[^:]:%19s",s)==1){ //check return of sscanf
/* %*[^:] will read string till ':' and discard it */
printf("%s",s);
}
This will store your desired string in s.
Note - Data in your file should in form as you show in question.
If you want data even outside loop then use a 2-d char array instead . Increment index of array in loop as you read new data from file.
EDIT-
Dont use uninitialized variable slen before calculating string length . You can re-write your code as -
int i=0;
if(sscanf(line,"%*[^:]:%19s",s)==1)
{
slen = strlen(s);
while(i<slen)/*encrypting firstname*/
{
//slen = strlen(s);
s[i] = (char)((s[i] - 'a' - 4) % 26 + 'a');
if(s == '\0')
{
break;
}
i++;
}
printf("%s",s);
slen--;
}

how to store strings inside an array in c

How can I store a list of strings in an array in c. cause usually the a string e.g:'string' is stored as s|t|r|i|g in array(?). now if i have an array that has 10 index and want to store string in each one of those indexes, how do i do it? if not doable, what other data structure can i use for it.
e.g: array = 'string1'|'string2'|..
i have done something, but its not working:
// Array for name of alphabets, and name of states
35 char *nameOfStates[numStates];
36 char buffer[3];
37
38 // Read the nameOfStates
39 int i;
40 for(i=0;i<numStates;i++){
41 printf("Name of STATES:");
42
43 int z=0;
44 char *buffer2;
45 while(z<2 && fgets(buffer,2,stdin) != NULL){
46
47 buffer2 = strndup(buffer,2);
48 z++;
49 }// End while-loop
50 nameOfStates[i] = buffer2;
51
52 }// End of for-loop to read nameOfStates
EDIT: I realized that array[size] doesn't actually work! I did it cause of my java backgroun d and i thought it might work. so i changed the program but it still is throwing segmentation fault. I post the full(edited) program below:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
// Function declaration
void analyze(char *string);
void clearNewLines(void);
int main(int argc, char *argv[]){
// Number of states and number of alphabets of DFA
int numStates;
int numAlphabets;
// Read numStates
printf("Enter the number of STATES:");
scanf("%d",&numStates);
// Flush STDIN
clearNewLines();
// Array for name of alphabets, and name of states
char **nameOfStates = malloc(numStates*sizeof(char*));
char *buffer = NULL;
// Read the nameOfStates
int i;
for(i=0;i<numStates;i++){
printf("Name of STATES:");
fgets(nameOfStates[i],2*sizeof(char),stdin);
}// End of for-loop to read nameOfStates
clearNewLines();
// Read numAlphabets
printf("Enter the number of ALPHABETS: ");
scanf("%d", &numAlphabets);
// Flush STDIN
clearNewLines();
// Array for name of alphabets, and name of states
char nameOfAlphabets[numAlphabets];
// Saving transition table
char *transitionTable[numStates][numAlphabets];
// Read name of alphabets
int j;
for(j=0;j<numAlphabets;j++){
printf("Name of ALPHABETS:");
nameOfAlphabets[j] = getchar();
// Flush STDIN
clearNewLines();
}// End for-loop to read alphabets
// Get the transitionTable[states][alphabets]
int row;
for(row=0;row<numStates;row++){
int col;
for(col=0;col<numAlphabets;col++){
printf("Enter Transition From %s to %c: ",nameOfStates[row],nameOfAlphabets[col]);
printf("\n");
}
}
return 0;
}// End of main function
/*
*
* clearNewLines - clear any newline character present at the STDIN
*/
void clearNewLines(void)
{
int c;
do
{
c = getchar();
} while (c != '\n' && c != EOF);
}
First: you cannot define an array size with a variable. I mean: char buf[variable]; doesn't work.
You have to do like this:
char **buf;
buf = malloc(sizeof(char) * number_of_strings);
if (buf == NULL)
return (MALLOC_ERROR);
Or with a macro like this:
// in your header file
#define BUF_SIZE 12
// in your .c file
char *buf[BUF_SIZE];
Then you also have to malloc the 2nd dimension of your array.
For example :
int i;
i = 0
while (buf[i])
{
buf[i] = malloc(sizeof(char) * string_length);
if (buf[i] == NULL)
return (MALLOC_ERROR);
i++;
}
And don't forget to free all dimensions of your array.
An array of arrays is the way to go.
// Array of size 5 (Don't forget to free!)
char **arrayOfStrings = malloc(5*sizeof(char*));
char *aString = "Hi";
arrayOfStrings[0] = aString;
//Literals work too
arrayOfStrings[1] = "Hallo";
aString = "Ahoy";
arrayOfStrings[2] = aString;
ArrayOfStrings values at end: Hi | Hallo | Ahoy | | |

Resources