issues putting char pointers into 2d array of char pointers - c

I'd love some help here, driving me nuts.
Large file using strtok to split up my lines. I'm trying to separate the first part out, save it into my symbolTblChar[lblCnt]. What I can't seem to understand is why, after pulling from the file , my output becomes so weird
INPUT
"SPACE \n LINE \n A1 \n A2"
Code
char* symbolTblChar[MAX_SYMBOLS][100];
int lblCnt = 0;
char line[LINE_SIZE];
char* chk;
while(fgets(line, sizeof line, fp) != NULL) {
chk = (char*)strtok( line, delims );
printf("Adding %s to symbol table", chk);
*symbolTblChar[lblCnt]=chk + '\0';
lblCnt++;
int t;
for(t = 0; t < lblCnt; t++)
printf("%s\n", *symbolTblChar[t]);
}
Output:
Adding SPACE to symbol table
Adding LINE to symbol table
LINE
Adding A1 to symbol table
A1
A1
Adding A2 to symbol table
A2
A2
A2

You need to allocate and store characters. With your code you are storing pointer of character array line and it will get overwritten when you read subsequent lines.
You need to do something like
*symbolTblChar[lblCnt]= strdup(chk);
Also, not sure you need double char pointer as
char* symbolTblChar[MAX_SYMBOLS][100];
You can work with below which will store MAX_SYMBOLS number of strings.
char* symbolTblChar[MAX_SYMBOLS];

I think I understand what it is you are trying to do. In addition to Rohan's answer, you are also stumbling over the use of strtok. While it is somewhat a catch 22, since you are reading a string with symbols separated by newlines, you can still make strtok work. Understand though, when using strtok, your first call to strtok uses the pointer to the string as its first argument:
chk = strtok (line, delims);
while all subsequent calls to strtok use NULL as the first argument:
chk = strtok (NULL, delims);
What is nice about strtok is it was tailor-made for parsing an entire string in a for loop format. e.g.:
for (chk = strtok (line, delims); chk; chk = strtok (NULL, delims))
Putting that together, and cleaning up symbolTblChar[MAX_SYMBOLS] to simply be an array of pointers to char, rearranging your logic a bit, provides the following example. I guessed at what you would need for LINE_SIZE and what would work for MAX_SYMBOLS (adjust as required):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINE_SIZE 128
#define MAX_SYMBOLS 32
int main (int argc, char **argv) {
char *symbolTblChar[MAX_SYMBOLS] = {NULL};
char line[LINE_SIZE] = {0};
char *chk = NULL;
char delims[] = " \n";
int lblCnt = 0;
int t = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (fp != stdin && !fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
printf ("\nCollecting Symbols:\n");
while (fgets (line, LINE_SIZE, fp))
{
for (chk = strtok (line, delims); chk; chk = strtok (NULL, delims)) {
printf (" Adding %s to symbol table\n", chk);
symbolTblChar[lblCnt] = strdup (chk);
lblCnt++;
/* check for MAX_SYMBOLS */
if (lblCnt == MAX_SYMBOLS) {
fprintf (stderr, "warining: MAX_SYMBOLS limit reached.\n");
break;
}
}
}
/* close file if not stdin */
if (fp != stdin) fclose (fp);
/* output */
printf ("\nSymbols:\n");
for (t = 0; t < lblCnt; t++)
printf(" %s\n", symbolTblChar[t]);
/* free allocated memory */
for (t = 0; t < lblCnt; t++)
free (symbolTblChar[t]);
return 0;
}
Output
Using your sample input provides:
$ printf "SPACE \n LINE \n A1 \n A2\n" | ./bin/symbltbl
Collecting Symbols:
Adding SPACE to symbol table
Adding LINE to symbol table
Adding A1 to symbol table
Adding A2 to symbol table
Symbols:
SPACE
LINE
A1
A2
Note: you can of course remove the Adding X ... intermediate print line when you are done with it. Now, even though it may not be apparent, you need to free the memory associate with each symbol added to symbolTblChar using strdup (which both allocates and copies its argument to a new memory location). You can see that taking place at the end of main.
If this isn't what you intended, let me know. After looking at the question and what you were doing, this seemed like your logical intent. Let me know if you have any questions.
File Input: note, you can also provide an input filename as the first argument to the program and the program will read from the file instead of stdin. For example, the input file:
$ cat symbols.txt
SPACE
LINE
A1
A2
Output Reading from File
$ /bin/symbltbl symbols.txt
Collecting Symbols:
Adding SPACE to symbol table
Adding LINE to symbol table
Adding A1 to symbol table
Adding A2 to symbol table
Symbols:
SPACE
LINE
A1
A2

Related

Read stdin with fgets, fill each line in an array and print each line

I want to read the stdin with fgets.
Then, I want to read each line. My lines are strings separated with ' '.
For example, this could be a line: 1 2 ab
I thought than I should use a malloc to count the number of params in my line because this number can vary from a line to another.
1 2 3 4 has 4 but a b 2 has 3.
I cut the line with strtok and then I fill my malloc with the tokens and I print them.
The final result is only to print each lines.
For example, this is a file.txt:
1 2 33 4
a b1 c
4 b l 11
And I do:
$ cat file.txt | ./a.out
It should print:
1 2 33 4
a b1 c
4 b l 11
but it doesn't!
Can you guys help me with this please :O
Furthermore, I want to use array to count the tokens and to work with them later. For example, I need to work separately with all the second param of each line, so array[1].
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv) {
while (fgets(argv, sizeof(argv), stdin) != NULL) {
char *array = (char *)malloc(sizeof(argv));
char *token = strtok(argv, " ");
strtok(token, "\n");
int i = 0;
while (token != NULL) {
array[i] = token;
token = strtok(NULL, " ");
printf("%d\n", array[i]);
++i;
}
}
return 0;
}
If all you want to do is count the tokens and output them, then you can simply use a pointer to the token and do not need to allocate storage. Using strtok() would be a good way to go. (note: if you have empty fields, you will need to parse using another method as strtok() will consider sequential delimiters as a single delimiter). The string passed to strtok() must be mutable as strtok() modifies the string (make a copy if you need to preserve the original)
Your approach is to read each line with fgets() into a sufficiently sized buffer (character array), zero your counter and then tokenize the line, incrementing the count for each token and output the token separated by a space.
You could do that as follows:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char line[MAXC]; /* storage for line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (line, MAXC, fp)) {
size_t toks = 0,
len;
line[(len = strcspn (line, "\n"))] = 0; /* trim \n, save length */
for (char *p = strtok (line, " \t"); p; p = strtok (NULL, " \t"))
printf (toks++ ? " %s" : "%s", p);
printf ("%*s\t(%zu tokens)\n", (int)len % 8, " ", toks);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Above a ternary is used to control the output of the space-separator (nothing before the first token -- when the count is zero, and then a single space before each subsequent token). The count of the tokens is appended to the end of each line.
(the strlen() of each line is just used to tidy up the spacing between the end of each output line and the appended count by adding the fractional part of a tab, if needed)
Example Use/Output
With your example data in the file dat/tokfile.txt, you would receive:
$ ./bin/fgets_tok_count dat/tokfile.txt
1 2 33 4 (4 tokens)
a b1 c (3 tokens)
4 b l 11 (4 tokens)
By taking the filename as the first argument to the program, or reading from stdin by default if no argument is given, you can redirect information to your program as well, e.g.
$ ./bin/fgets_tok_count < dat/tokfile.txt
Or heaven forbid your UUOc form will also work:
$ cat dat/tokfile.txt | ./bin/fgets_tok_count
Dynamically Storing Unknown Number of Tokens Per-Line
To dynamically stored each token and preserve each for the duration of your tokenization loop, then all you need is a pointer-to-pointer-to char and a counter to track the number of pointers and strings allocated. You can do that similar to:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char line[MAXC]; /* storage for line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (line, MAXC, fp)) {
char **tokens = NULL;
size_t len = 0,
toks = 0;
line[(len = strcspn (line, "\n"))] = 0; /* trim \n, save length */
/* loop over each token */
for (char *p = strtok (line, " \t"); p; p = strtok (NULL, " \t")) {
size_t toklen = strlen (p);
/* allocate/validate 1 additional pointer for tokens */
void *tmp = realloc (tokens, (toks + 1) * sizeof *tokens);
if (!tmp) {
perror ("realloc-tokens");
break;
}
tokens = tmp;
/* allocate/validate storage for token of len + 1 */
if (!(tokens[toks] = malloc (toklen + 1))) {
perror ("malloc-tokens[toks]");
break;
}
/* copy token to allocated block */
memcpy (tokens[toks], p, toklen + 1);
toks++; /* increment no. of tokens in line */
}
/* output all stored line tokens and no. of tokens */
for (size_t i = 0; i < toks; i++) {
printf (i ? " %s" : "%s", tokens[i]);
free (tokens[i]); /* done with stored token, free token */
}
free (tokens); /* free pointers */
printf ("%*s\t(%zu tokens)\n", (int)len % 8, " ", toks);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
(the program output is the same)
Basically, above, realloc is used to allocate storage for 1-additional pointer each time a token is found, malloc is then used to allocate for the length of the token (+1), and then the token is copied to that allocated block. When you are done tokenizing the line, the tokens pointer points to a block of memory containing toks pointers, to which a block of memory holding each token was assigned in turn. After all tokenization and storage is complete, the same output is produced by looping over the pointers, outputting the tokens (and the number of tokens). All memory is then freed.
Look things over and let me know if you have further questions.

how to get specific rows from csv file with exact data using c?

This is my csv file, i want to get only those row which start with character "A" so i got my output but with some addition column as '0' please help me to find were i went wrong?
And one more thing i want to remove specific column like bread,anName,ot
Name,id,bread,anName,Ot,number
A,1,animal,tiger,op,8.1
M,2,animal,toper,ip,9.1
A1,7,animal,dog,cp,Na11
A2,9,animal,mouse,ap,0
A23,9,animal,pouch,gp,Na11
#include <stdio.h>
#include <stdlib.h>
#define NUMLETTERS 100
typedef struct {
char Name[100];
int id;
char number[100];
} record_t;
int main(void) {
FILE *fp;
record_t records[NUMLETTERS];
int count = 0, i;
fp = fopen("letter.csv", "r");
if (fp == NULL) {
fprintf(stderr, "Error reading file\n");
return 1;
}
while (fscanf(fp, "%s,%d,%s", records[count].name, &records[count].id, records[count].number) == 1)
count++;
for (i = 0; i < count; i++) {
if(records[i].Name[0] == 'A'){
printf("%s,%d,%s\n", records[i].Name, records[i].id, records[i].number);
}
}
fclose(fp);
return 0;
}
i want output as:
A,1,8.1
A1,7,Na11
A2,9,0
A23,9,Na11
You have two problems:
The %s format specifier tells fscanf to read a space-delimited string. Since the the records aren't space-delimited the first %s will read the whole line.
The fscanf function returns the number of successfully parsed elements it handled. Since you attempt to read three values you should compare with 3 instead of 1.
Now for one way how to solve the first problem: Use the %[ format specifier. It can handle simple patterns and, most importantly, negative patterns (read while input does not match).
So you could tell fscanf to read a string until it finds a comma by using %[^,]:
fscanf(fp, " %[^,],%d,%s", records[count].Refdes, &records[count].pin, records[count].NetName)
The use of the %[ specifier is only needed for the first string, as the second will be space-delimited (the newline).
Also note that there's a space before the %[ format, to read and ignore leading white-space, like for example the newline from the previous line.
i want to get only those row which start with character "A"
i want to remove the number which coming between A and tiger,
If I understand you correctly and you only want to store rows beginning with 'A', then I would adjust your approach to read each line with fgets() and then check whether the first character in the buffer is 'A', if so, continue; and get the next line. The for those lines that do start with 'A', simply use sscanf to parse the data into your array of struct records.
For your second part of removing the number between 'A' and "tiger", there is a difference between what you store and what you output (this comes into play in storing only records beginning with 'A' as well), but for those structs stored where the line starts with 'A', you can simply not-output the pin struct member to get the output you want.
The approach to reading a line at a time will simply require that you declare an additional character array (buffer), called buf below, to read each line into with fgets(), e.g.
char buf[3 * NUMLETTERS] = "";
...
/* read each line into buf until a max of NUMLETTERS struct filled */
while (count < NUMLETTERS && fgets (buf, sizeof buf, fp)) {
record_t tmp = { .Refdes = "" }; /* temporary struct to read into */
if (*buf != 'A') /* if doesn't start with A get next */
continue;
/* separate lines beginning with 'A' into struct members */
if (sscanf (buf, " %99[^,],%d,%99[^\n]",
tmp.Refdes, &tmp.pin, tmp.NetName) == 3)
records[count++] = tmp; /* assign tmp, increment count */
else
fprintf (stderr, "%d A record - invalid format.\n", count + 1);
}
A short example putting that to use and (since we are not sure what "remove" is intended to be), we have included a pre-processor conditional that will only output the .Refdes and .NetName members by default, but if you either #define WITHPIN or include the define in your compile string (e.g. -DWITHPIN) it will output the .pin member as well.
#include <stdio.h>
#include <stdlib.h>
#define NUMLETTERS 100
typedef struct {
char Refdes[NUMLETTERS];
int pin;
char NetName[NUMLETTERS];
} record_t;
int main (int argc, char **argv) {
record_t records[NUMLETTERS];
char buf[3 * NUMLETTERS] = "";
int count = 0, i;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
/* read each line into buf until a max of NUMLETTERS struct filled */
while (count < NUMLETTERS && fgets (buf, sizeof buf, fp)) {
record_t tmp = { .Refdes = "" }; /* temporary struct to read into */
if (*buf != 'A') /* if doesn't start with A get next */
continue;
/* separate lines beginning with 'A' into struct members */
if (sscanf (buf, " %99[^,],%d,%99[^\n]",
tmp.Refdes, &tmp.pin, tmp.NetName) == 3)
records[count++] = tmp; /* assign tmp, increment count */
else
fprintf (stderr, "%d A record - invalid format.\n", count + 1);
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
for (i = 0; i < count; i++)
#ifdef WITHPIN
printf ("%-8s %2d %s\n",
records[i].Refdes, records[i].pin, records[i].NetName);
#else
printf ("%-8s %s\n", records[i].Refdes, records[i].NetName);
#endif
}
Example Use/Output
$ ./bin/getaonly dat/getasonly.txt
A tiger
A1 dog
A2 mouse
A23 pouch
If you define -DWITHPIN in your compile string, then you will get all three outputs:
$ ./bin/getaonly dat/getasonly.txt
A 1 tiger
A1 7 dog
A2 9 mouse
A23 9 pouch
(note: with the data stored in your array, you can adjust the output format to anything you need)
Since there is some uncertainty whether you want to store all and output only records beginning with 'A' or only want to store records beginning with 'A' -- let me know if I need to make changes and I'm happy to help further.

How to fscanf word by word in a file?

I have a file with a series of words separated by a white space. For example file.txt contains this: "this is the file". How can I use fscanf to take word by word and put each word in an array of strings?
Then I did this but I don't know if it's correct:
char *words[100];
int i=0;
while(!feof(file)){
fscanf(file, "%s", words[i]);
i++;
fscanf(file, " ");
}
When reading repeated input, you control the input loop with the input function itself (fscanf in your case). While you can also loop continually (e.g. for (;;) { ... }) and check independently whether the return is EOF, whether a matching failure occurred, or whether the return matches the number of conversion specifiers (success), in your case simply checking that the return matches the single "%s" conversion specifier is fine (e.g. that the return is 1).
Storing each word in an array, you have several options. The most simple is using a 2D array of char with automatic storage. Since the longest non-medical word in the Unabridged Dictionary is 29-characters (requiring a total of 30-characters with the nul-terminating character), a 2D array with a fixed number of rows and fixed number of columns of at least 30 is fine. (dynamically allocating allows you to read and allocate memory for as many words as may be required -- but that is left for later.)
So to set up storage for 128 words, you could do something similar to the following:
#include <stdio.h>
#define MAXW 32 /* if you need a constant, #define one (or more) */
#define MAXA 128
int main (int argc, char **argv) {
char array[MAXA][MAXW] = {{""}}; /* array to store up to 128 words */
size_t n = 0; /* word index */
Now simply open your filename provided as the first argument to the program (or read from stdin by default if no argument is given), and then validate that your file is open for reading, e.g.
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
Now to the crux of your read-loop. Simply loop checking the return of fscanf to determine success/failure of the read, adding words to your array and incrementing your index on each successful read. You must also include in your loop-control a check of your index against your array bounds to ensure you do not attempt to write more words to your array than it can hold, e.g.
while (n < MAXA && fscanf (fp, "%s", array[n]) == 1)
n++;
That's it, now just close the file and use your words stored in your array as needed. For example just printing the stored words you could do:
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < n; i++)
printf ("array[%3zu] : %s\n", i, array[i]);
return 0;
}
Now just compile it, With Warnings Enabled (e.g. -Wall -Wextra -pedantic for gcc/clang, or /W3 on (VS, cl.exe) and then test on your file. The full code is:
#include <stdio.h>
#define MAXW 32 /* if you need a constant, #define one (or more) */
#define MAXA 128
int main (int argc, char **argv) {
char array[MAXA][MAXW] = {{""}}; /* array to store up to 128 words */
size_t n = 0; /* word index */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (n < MAXA && fscanf (fp, "%s", array[n]) == 1)
n++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
for (size_t i = 0; i < n; i++)
printf ("array[%3zu] : %s\n", i, array[i]);
return 0;
}
Example Input File
$ cat dat/thefile.txt
this is the file
Example Use/Output
$ ./bin/fscanfsimple dat/thefile.txt
array[ 0] : this
array[ 1] : is
array[ 2] : the
array[ 3] : file
Look things over and let me know if you have further questions.
strtok() might be a function that can help you here.
If you know that the words will be separated by whitespace, then calling strtok will return the char pointer to the start of the next word.
Sample code from https://www.systutorials.com/docs/linux/man/3p-strtok/
#include <string.h>
...
char *token;
char *line = "LINE TO BE SEPARATED";
char *search = " ";
/* Token will point to "LINE". */
token = strtok(line, search);
/* Token will point to "TO". */
token = strtok(NULL, search);
In your case, the space character would also act as a delimiter in the line.
Note that strtok might modify the string passed in, so if you need to you should make a deep copy using something like malloc.
It might also be easier to use fread() to read a block from a file
As mentioned in comments, using feof() does not work as would be expected. And, as described in this answer unless the content of the file is formatted with very predictable content, using any of the scanf family to parse out the words is overly complicated. I do not recommend using it for that purpose.
There are many other, better ways to read content of a file, word by word. My preference is to read each line into a buffer, then parse the buffer to extract the words. This requires determining those characters that may be in the file, but would not be considered part of a word. Characters such as \n,\t, (space), -, etc. should be considered delimiters, and can be used to extract the words. The following is a recipe for extracting words from a file: (example code for a few of the items is included below these steps.)
Read file to count words, and get the length of the longest word.
Use count, and longest values from 1st step to allocate memory for words.
Rewind the file.
Read file line by line into a line buffer using while(fgets(line, size, fp))
Parse each new line into words using delimiters and store each word into arrays of step 2.
Use resulting array of words as necessary.
free all memory allocated when finished with arrays
Some example of code to do some of these tasks:
// Get count of words, and longest word in file
int longestWord(char *file, int *nWords)
{
FILE *fp=0;
int cnt=0, longest=0, numWords=0;
int c;
fp = fopen(file, "r");
if(fp)
{
// if((strlen(buf) > 0) && (buf[0] != '\t') && (buf[0] != '\n') && (buf[0] != '\0')&& (buf[0] > 0))
while ( (c = fgetc(fp) ) != EOF )
{
if ( isalnum (c) ) cnt++;
else if ( ( ispunct (c) ) || ( isspace(c) ) || (c == '\0' ))
{
(cnt > longest) ? (longest = cnt, cnt=0) : (cnt=0);
numWords++;
}
}
*nWords = numWords;
fclose(fp);
}
else return -1;
return longest;
}
// Create indexable memory for word arrays
char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
Usage: For a file with 25 words, the longest being 80 bytes:
char **strArray = Create2DStr(25, 80+1);//creates 25 array locations
//each 80+1 characters long
//(+1 is room for null terminator.)
int i=0;
char words[50][50];
while(fscanf(file, " %s ", words[i]) != EOF)
i++;
I wouldn't entirely recommend doing it this way, because of the unknown amount of words in the file, and the unknown length of a "word". Either can be over the size of '50'. Just do it dynamically, instead. Still, this should show you how it works.
How can I use fscanf to take word by word and put each word in an array of strings?
Read each word twice: first to find length via "%n". 2nd time, save it. (Inefficient yet simple)
Re-size strings as you go. Again inefficient, yet simple.
// Rough untested sample code - still need to add error checking.
size_t string_count = 0;
char **strings = NULL;
for (;;) {
long pos = ftell(file);
int n = 0;
fscanf(file, "%*s%n", &n); // record where scanning a "word" stopped
if (n == 0) break;
fseek(file, pos, SEEK_SET); // go back;
strings = realloc(strings, sizeof *strings * (string_count+1));// increase array size
strings[string_count] = malloc(n + 1u); // Get enough memory for the word
fscanf(file, "%s ", strings[string_count] ); // read/save word
}
// use strings[], string_count
// When done, free each strings[] and then strings

Splitting Strings from file and putting them into array causes program crash

I am trying to read a file line by line and split it into words. Those words should be saved into an array. However, the program only gets the first line of the text file and when it tries to read the new line, the program crashes.
FILE *inputfile = fopen("file.txt", "r");
char buf [1024];
int i=0;
char fileName [25];
char words [100][100];
char *token;
while(fgets(buf,sizeof(buf),inputfile)!=NULL){
token = strtok(buf, " ");
strcpy(words[0], token);
printf("%s\n", words[0]);
while (token != NULL) {
token = strtok(NULL, " ");
strcpy(words[i],token);
printf("%s\n",words[i]);
i++;
}
}
After good answer from xing I decided to write my FULL simple program realizing your task and tell something about my solution. My program reads line-by-line a file, given as input argument and saves next lines into a buffer.
Code:
#include <assert.h>
#include <errno.h>
#define _WITH_GETLINE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define assert_msg(x) for ( ; !(x) ; assert(x) )
int
main(int argc, char **argv)
{
FILE *file;
char *buf, *token;
size_t length, read, size;
assert(argc == 2);
file = fopen(argv[1], "r");
assert_msg(file != NULL) {
fprintf(stderr, "Error ocurred: %s\n", strerror(errno));
}
token = NULL;
length = read = size = 0;
while ((read = getline(&token, &length, file)) != -1) {
token[read - 1] = ' ';
size += read;
buf = realloc(buf, size);
assert(buf != NULL);
(void)strncat(buf, token, read);
}
printf("%s\n", buf);
fclose(file);
free(buf);
free(token);
return (EXIT_SUCCESS);
}
For file file.txt:
that is a
text
which I
would like to
read
from file.
I got a result:
$ ./program file.txt
that is a text which I would like to read from file.
Few things which is worth to say about that solution:
Instead of fgets(3) I used getline(3) function because of easy way to knowledge about string length in line (read variable) and auto memory allocation for got string (token). It is important to remember to free(3) it. For Unix-like systems getline(3) is not provided by default in order to avoid compatibility problems. Therefore, #define _WITH_GETLINE macro is used before <stdio.h> header to make that function available.
buf contains only mandatory amount of space needed to save string. After reading one line from file buf is extended by the required amount of space by realloc(3). Is it a bit more "universal" solution. It is important to remember about freeing objects allocated on heap.
I also used strncat(3) which ensures that no more than read characters (length of token) would be save into buf. It is also not the best way of using strncat(3) because we also should testing a string truncation. But in general it is better than simple using of strcat(3) which is not recommended to use because enables malicious users to arbitrarily change a running program's functionality through a buffer overflow attack. strcat(3) and strncat(3) also adds terminating \0.
A getline(3) returns token with a new line character so I decided to replace it from new line to space (in context of creating sentences from words given in file). I also should eliminate last space but I do not wanted to complicate a source code.
From not mandatory things I also defined my own macro assert_msg(x) which is able to run assert(3) function and shows a text message with error. But it is only a feature but thanks to that we are able to see error message got during wrong attempts open a file.
The problem is getting the next token in the inner while loop and passing the result to strcpy without any check for a NULL result.
while(fgets(buf,sizeof(buf),inputfile)!=NULL){
token = strtok(buf, " ");
strcpy(words[0], token);
printf("%s\n", words[0]);
while (token != NULL) {//not at the end of the line. yet!
token = strtok(NULL, " ");//get next token. but token == NULL at end of line
//passing NULL to strcpy is a problem
strcpy(words[i],token);
printf("%s\n",words[i]);
i++;
}
}
By incorporating the check into the while condition, passing NULL as the second argument to strcpy is avoided.
while ( ( token = strtok ( NULL, " ")) != NULL) {//get next token != NULL
//if token == NULL the while block is not executed
strcpy(words[i],token);
printf("%s\n",words[i]);
i++;
}
Sanitize your loops, and don't repeat yourself:
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *inputfile = fopen("file.txt", "r");
char buf [1024];
int i=0;
char fileName [25];
char words [100][100];
char *token;
for(i=0; fgets(buf,sizeof(buf),inputfile); ) {
for(token = strtok(buf, " "); token != NULL; token = strtok(NULL, " ")){
strcpy(words[i++], token);
}
}
return 0;
}

Read Txt file Language C

Hi guys I have this file struct:
0
2 4
0: 1(ab) 5(b)
1: 2(b) 6(a)
2: 0(a) 2(b)
3: 2(a) 6(b)
4: 5(ab)
5: 2(a) 6(b)
6: 4(b) 6(ab)
Each line will feed a struct with its data (numbers + letters).
What's the best way to read the line and get the strings I want?
Example:
0
2 4
0,1,ab,5,b
1,2,b,5,a
...
The lines may vary in size because we can have 1, 2, 3, .... numbers.
I already did it :
//struct
#define MAX_ 20
struct otherstats{ //struct otherStats
int conectstat[MAX_];//conection with others stats
int transitions[MAX_];//Symbols betwen conection ASCI
}tableStats[MAX_];
struct sAutomate{
int stat_initial; //initial
int stats_finals[MAX_]; //final orfinals
struct otherstats tableStats[MAX_]; //otherStats 0 1 2 3 4 5 6
};
/* eXample that what i want ..using the example
sAutomate.stat_initial=0
sAutomate.stats_finals[0]=2
sAutomate.stats_finals[1]=4
Others Stats table
//0
sAutomate.tableStats[0].conectstat[0]=1;
sAutomate.tableStats[0].conectstat[1]=5;
sAutomate.tableStats[0].transitions[0]=ab;
sAutomate.tableStats[0].transitions[1]=b;
//1
sAutomate.tableStats[1].conectstat[0]=2;
sAutomate.tableStats[1].conectstat[1]=6;
sAutomate.tableStats[1].transitions[0]=b;
sAutomate.tableStats[1].transitions[1]=a;
///etc
*/
void scanfile(){ //function to read the file
struct sAutomate st; //initialize st struct
char filename[] = "txe.txt";
FILE *file = fopen ( filename, "r" );
char buf[81];
char parts[5][11];
fscanf(file,"%d", &st.stat_initial);//read first line
printf("initial state : %d \n", st.stat_initial);
fscanf(file,"%d",&st.stats_finals);
fscanf(file,"%d",&st.stats_finals);
while (fgets(buf, sizeof(buf), stdin) != NULL)
{
if (sscanf(buf, "%10[^:]: (%10[^(], %10[^)]), (%10[^(], %10[^)])",
parts[0], parts[1], parts[2], parts[3], parts[4]) == 5)
{
printf("parts: %s, %s, %s, %s, %s\n",
parts[0], parts[1], parts[2], parts[3], parts[4]);
}
else
{
printf("Invalid input: %s", buf);
}
}
//fclose
First problem I see is you're overwriting stats_finals:
fscanf(file,"%d",&st.stats_finals);
fscanf(file,"%d",&st.stats_finals);
What you wanted to do here was:
fscanf(file,"%d",&st.stats_finals[0]);
fscanf(file,"%d",&st.stats_finals[1]);
To save off both the "2" and the "4" from the text file.
Second major problem is you're reading from stdin:
while (fgets(buf, sizeof(buf), stdin) != NULL)
That doesn't read your text file, that reads input from the keyboard... So you wanted that to be:
while (fgets(buf, sizeof(buf), file) != NULL)
Third (minor) problem is that fscanf() will not read newlines, and fgets() will. This means when you go from reading your second stats_finals to the first read in the while loop, your first input will just be the left over newline character. That's not a big deal since you check for "invalid input", but it's worth noting.
Finally, your sscanf looks wrong to me:
sscanf(buf, "%10[^:]: (%10[^(], %10[^)]), (%10[^(], %10[^)])",
^ ^
That's a width of 10, Why are you checking for commas? You didn't
I don't think that's have any in your text file
what you wanted...
I think this is more what you were looking for:
sscanf(buf, "%[0-9]: %[0-9](%[^)]) %[0-9](%[^)])",
^
takes a digit (0 to 9)
EDIT
Missed your original point. If you don't know how long the strings will be that you're reading, you can't use sscanf(). It's that simple. :)
The scanf family assumes you know how many objects you'll be parsing and the format string takes in that many. There are other options however.
Read a single line with fgets as you're doing, but then you can tokenize it. Either with the C function strtok or by your own hand with a for loop.
One note however:
Since you don't know how long it is, this: char parts[5][11]; is not your best bet. This limits you to 2 entries... probably it would be better to do this dynamically (read the line then allocate the correct size to store your tokens in.)
If you really don't know how many numbers and letters the line will contain, why are you reading a fixed amount of numbers and letters?
You could read the whole line with fgets and then parse it with a tokenizer like strtok, something like this:
const char* const DELIMITERS = " ";
int i; // index for tableStats
char* token;
token = strtok(line, DELIMITERS);
// first integer
if (token == NULL || sscanf(token, "%d:", &i) < 1)
// error
/* it seems like you should have at least one element in your "list",
* otherwise this is not necessary
*/
token = strtok(NULL, DELIMITERS);
if (token == NULL || sscanf(token, "%d(%[^)])",
&(tableStats[i].connectstat[0]),
&(tableStats[i].transitions[0])) < 2)
// error
// read optional part
for (int j = 1; (token = strtok(NULL, DELIMITERS)) != NULL; ++j)
if (sscanf(token, "%d(%[^)])", &(tableStats[i].connectstat[j]),
&(tableStats[i].transitions[j])) < 3)
break;
Remember that strtok changes the string, make a copy of it if you still need it.
Obviusly the code is for the arbitrary long lines, reading the first two lines is trivial.

Resources