String arrays in c - c

I wrote a code to read files
What is wrong in the following code I am always getting last filename if I print any arrayItem
#include <stdio.h>
#include <string.h>
char **get_files()
{
FILE *fp;
int status;
char file[1000];
char **files = NULL;
int i = 0;
/* Open the command for reading. */
fp = popen("ls", "r");
if (fp == NULL) {
printf("Failed to run command\n" );
//exit;
}
while (fgets(file, sizeof(file)-1, fp) != NULL) {
files = (char **)realloc(files, (i + 1) * sizeof(char *));
//files[i] = (char *)malloc(sizeof(char));
files[i] = file;
i++;
}
printf("%s", files[0]);
return files;
}
int main()
{
char **files = NULL;
int i =0 ;
files = get_files("");
}

you should use
files[i] = strdup(file);
instead of
files[i] = file;
The second version only lets files[i] point to your reading buffer which is always the same. With the next fgets, you'll overwrite the contents of file and thus the contents of file[i] which actually point to the same location in memory.
In fact, at the end, all your file[0]..file[n] will point to the same location as file does.
With strdup(..) you're allocating a new buffer and copying the contents of file there.

pclose is missing for your popen. popen is only POSIX, not C89/C99.
No memory-alloc check in the example, its your work ;-)
#include <stdio.h>
#include <stdlib.h>
char **get_files(char **list)
{
FILE *fp;
char file[1000];
int i=1;
/* Open the command for reading. */
fp = popen("ls -l", "rt");
if( !fp )
perror("Failed to run command\n" ),exit(1);
while( fgets(file, sizeof file , fp) ) {
list = realloc(list, ++i * sizeof*list );
memmove( list+1, list, (i-1)*sizeof*list);
*list = strcpy( malloc(strlen(file)+1), file);
}
pclose( fp );
return list;
}
main()
{
char **files = get_files(calloc(1,sizeof*files)), **start=files;
while( *files )
{
puts(*files);
free(*files++);
}
free(start);
return 0;
}

Calling popen() on 'ls' is a bad way to do this. Take a look at opendir(), readdir(), rewinddir() and closedir().

You are reusing the file array. After you've read a filename, you need to use strdup to take a copy of it, and put that copy into the files array. Otherwise, every element in files just points to the same string.

Your array in char file[1000] is single dimension, regardless of how you re-allocate memory (unless I'm missing something obvious). If you are reading an unknown number of files, then a linked list is probably the best way to go about this.

Related

opening and closing a text file many times and read from the continuation

I have a text file, and I open it and read one line of it, and close the text file. I'm calling my function under a for loop, but each time this function reads the first line of a text file, how can I fix it to read from the continuation
You can use fseek to reposition yourself in the file after closing and reopening, but it is very unusual to do so. So unusual, in fact, that I would suggest it is completely wrong. Here's some sample code that demonstrates how to do that, as well as a more typical loop. Each loop here reads the first 2 lines of the file, assuming each line is sufficiently small; handling long lines is beyond the scope of this question.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
FILE * xfopen(const char *path, const char *mode);
void xfseek(FILE *stream, long offset, int whence, const char *);
long xftell(FILE *stream, const char *);
void xfclose(FILE *stream, const char *);
int
main(int argc, char *argv[])
{
const char *path = argc > 1 ? argv[1] : "input";
/* Read the first two lines of the file, closing the file on each
* iteration. This is ** not ** the usual way to do this, and is
* included here for demonstration
* purposes only. DO NOT DO THIS.
* It is very unusual to close and re-open the file on each iteration.
*/
long position = 0;
for( int line = 1; line < 3; line++ ){
FILE *ifp = xfopen(path, "r");
char buf[1024];
xfseek(ifp, position, SEEK_SET, path);
fgets(buf, sizeof buf, ifp); /* (1) */
printf("line %d: %s", line, buf);
position = xftell(ifp, path);
xfclose(ifp, path); /* !! */
}
/* The more usual way to read each line of a file is to simply
* read it with repeated calls to the appropriate read method
* (fgets, fread, fgetc, etc.) Each subsequent read starts
* where the previous read finished.
*/
FILE *ifp = xfopen(path, "r");
for( int line = 1; line < 3; line++ ){
char buf[1024];
fgets(buf, sizeof buf, ifp); /* (1) */
printf("line %d: %s", line, buf);
}
xfclose(ifp, path);
return 0;
}
FILE *
xfopen(const char *path, const char *mode)
{
FILE *fp = path[0] != '-' || path[1] != '\0' ? fopen(path, mode) :
*mode == 'r' ? stdin : stdout;
if( fp == NULL ){
perror(path);
exit(EXIT_FAILURE);
}
return fp;
}
void
xfseek(FILE *stream, long offset, int whence, const char *name)
{
if( fseek(stream, offset, whence) == -1){
perror(name);
exit(EXIT_FAILURE);
}
}
long
xftell(FILE *stream, const char *name)
{
long ret = ftell(stream);
if( ret == -1 ){
perror(name);
exit(EXIT_FAILURE);
}
return ret;
}
void
xfclose(FILE *stream, const char *name)
{
if( fclose(stream) ){
perror(name);
exit(EXIT_FAILURE);
}
}
Notes: (1) It is left as an exercise for the reader how best to handle short reads (eg, when fgets returns NULL) or long lines (eg, when fgets completely fills the buffer but fails to read an entire line). Perhaps it is a bit of a cop-out to leave that as an exercise, but the annoyance of dealing with those issues points strongly towards reasons for using the standard idiom. If you want to print the first two lines of the file, use some variation of for( int count = 0; (c = fgetc(fp)) != NULL && count < 2; ) { if( c == '\n' ) count += 1; putchar(c); }. Putting the read function as a condition of the loop is (almost) always the best choice.
The comments have already made suggestions on other alternatives for what you are attempting. But regardless whether it is the right approach or not, it seems pretty clear that your stated ask is clear about wanting to use fseek() et. al to view successive lines when opening and closing a file.
To open and close a file, and each time access and display a successive line, you must first know where each of the locations to be viewed are located within that file. Indeed, as you have tagged, fseek(), (as well as ftell()) can be used to do this. The following pseudo code steps illustrate one possibility:
//store file pointer locations of each line in file:
FILE *fp = fopen(fn, "r");
if(fp)
{
for(int i = 0; i < l_cnt; i++)
{
pos[i] = ftell(fp);
fgets(line, sizeof line, fp);
}
}
fclose(fp);
Then...
//alternately open and close file to view successive lines at stored positions
for(int i = 0; i < line_cnt; i++)
{
FILE *fp = fopen(fn, "r");
if(fp)
{
fseek(fp, pos[i], 0);
fgets(line, sizeof line, fp);
printf("line %d: %s\n", i, line);
fclose(fp);
}
}
There is a more complete source and run-time example here

Using fgets to read through file in C

I am trying to read through the file given then tokenize it. The only problem im having is fgets.The file open recieves no errors. I have seen this elsewhere on the site however no matter how i set this up including setting fileLine to a set amount like (char fileline [200]) i get a segmentation fault. Thanks in advance for any help.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[]){
char *fileName = "0";
char *tokenize, *savePtr;
struct Record *database= malloc(sizeof(database[0]));
int recordNum =0;
char *fileLine = malloc(sizeof(char *));//have replaced with fileline[200] still didnt work
FILE *fd = open(fileName,O_RDWR);
if(fd< 0){
perror("ERROR OPENING FILE");
}
while(fgets(fileLine,200,fd) !=NULL){
printf("%s\n", fileLine);
tokenize = strtok_r(fileLine,",",&savePtr);
while(tokenize != NULL){
//TOKENIZING into a struct
}
}
Why use open() with FILE? Use fopen() instead.
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *fileName = "test.txt";
char *tokenize, *savePtr;
char fileLine[200] = {0}; // init this to be NULL terminated
FILE *fd = fopen(fileName, "r");
if (fd == 0) { // error check, equal to 0 as iharob said, not less than 0
perror("ERROR OPENING FILE");
return -1;
}
while (fgets(fileLine, 200, fd) != NULL) {
printf("%s\n", fileLine);
tokenize = strtok_r(fileLine, ",", &savePtr);
while (tokenize != NULL) {
tokenize = strtok_r(NULL, ",", &savePtr); // do not forget to pass NULL
//TOKENIZING into a struct
}
}
return 0;
}
As Weather Vane said, fd < 0 would work if you used open(). However, with fopen(), you should check to see if the pointer is NULL, ecquivalently fd == 0.
A comparison between this functions that open a file can be found in:
open and fopen function
C fopen vs open
The way I have it in mind is that fopen() is of higher level.
This line
char *fileLine = malloc(sizeof(char *));
allocates memory for a char * type, 4 or 8 bytes (depending on the platform).
So when you do
fgets(fileLine,200,fd)
it expects there to be 200 bytes of memory available.
Try this:
char *fileLine = malloc(200);
if (fileLine == NULL) { ... } // check for error
which will allocate the memory required.
You are using open() instead of fopen().
You can't be sure that the file did open correctly because fopen() does not return an integer, but a pointer to a FILE * object, on failure it returns NULL, so the right codition is
FILE *file;
file = fopen(filename, "r");
if (file == NULL)
{
perror("fopen()");
return -1;
}
In your code, you still go and use fgets() even when the fopen() fails, you should abort the program in that case.
Also, malloc() takes the number of bytes as the size parameter, so if you want fgets() to be limited to read just count bytes, then malloc() should be
char *buffer;
size_t count;
count = 200; /* or a value obtained someway */
buffer = malloc(count);
if (buffer == NULL)
{
fclose(file);
perror("malloc()");
return -1;
}
All the problems in your code would be pointed out by the compiler if you enable compilation warnings.

segfaulting with fgets, even though fopen doesn't return NULL in C

I've looked all over and made sure there were no warnings, but my code to replace text with digits keeps returning segfault. Any help?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc , char *argv[])
{
FILE *file;
file = fopen(argv[1] , "r");
char *line = malloc(1024);
if(file != NULL)
{
while(fgets(line , sizeof(line) , file))
{
//things
}
}
else
{
printf("ERROR: %s NOT AVAILABLE" , argv[1]);
}
return 0;
}
Replace:
char *line = malloc(1024);
with:
char line[1024] = {0};
or:
char line[1024];
if you don't want to clear out the line buffer.
Otherwise, you end up with two problems.
First:
sizeof(line)
returns the size of the pointer (4 or 8 bytes). That's not what you want.
Second: You have a memory leak because you don't free the line pointer at the end.
You can use malloc if you want, but you want to write clean(er) code to do this. You might do something like:
#define MAX_LINE_LENGTH 1024
/* ... */
char *line = NULL;
line = malloc(MAX_LINE_LENGTH);
if (!line) {
fprintf(stderr, "Error: Could not allocate space for line buffer!\n");
exit(EXIT_FAILURE);
}
FILE *file = NULL;
/* Avoid undefined behavior by making sure filename argument holds a value */
if (argv[1])
file = fopen(argv[1] , "r");
if (file != NULL) { /* You could also do "if (file) { ... }" */
while (fgets(line, MAX_LINE_LENGTH, file)) {
/* ... */
}
}
free(line);
line = NULL;
As a habit, explicitly initialize pointers to NULL, and check that they actually hold a value before using them. Welcome to C!

C - why do I get this segfault with my File I/O program?

I'm making a practice program to make a simple alteration to a variable in my Makefile while learning C. I get a segfault whenever I run this program, but I don't know why. I suspect it has something to do with the "r+" fopen mode or my use of fseek(). Here is the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void rewind(FILE *f)
{
long start = 0;
fseek(f, start, SEEK_SET);
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("arguments too many or too few. use: setfile <filename> (minus .c extension)\n");
exit(1);
}
FILE *mfile = fopen("Makefile", "r+"); // note to self: r+ is for a file that already exists
FILE *old_mfile = fopen("OLD.Makefile", "r+"); // w+ erases the file and starts in read-write mode with a fresh one
char line[200];
char *fn_ptr;
char *name = argv[1];
while(fgets(line, 199, mfile)) // first create the backup
{
fputs(line , old_mfile); // before changing the line, write it to the backup
}
rewind(mfile); // reset the files to position 0
rewind(old_mfile);
puts("Makefile backed-up as 'OLD.Makefile'");
while(fgets(line, 199, old_mfile)) // now lets loop again and rewrite with the new FNAME
{
if((fn_ptr = (strstr(line, "FNAME= "))))
{
fn_ptr += strlen("FNAME= ");
int i;
for(i = 0; i < strlen(name); i++)
{
*(fn_ptr+i) = *(name+i);
}
*(fn_ptr+i) = '\0';
}
// printf("%s", line); // for debugging
fputs(line , mfile);
}
printf("FNAME is now: '%s'\n", argv[1]);
fclose(mfile);
fclose(old_mfile);
return 0;
}
Check this line again:
FILE *old_mfile = fopen("OLD.Makefile", "r+"); // w+ erases the file and starts in read-write mode with a fresh one
You have the correct mode in the comment, but not in the fopen call.
How to not get the segmentation fault, besides changing the mode? Always check return values! If fopen fails it will return NULL.
Here is a working version. There are several subtle points to note here so I will leave you to examine them one by one by toggling the changes in and out. The man pages for the called functions are probably enough if carefully read.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void rewind(FILE *f)
{
long start = 0;
fseek(f, start, SEEK_SET);
}
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("arguments too many or too few. use: setfile <filename> (minus .c extension)\n");
exit(1);
}
FILE *mfile = fopen("Makefile", "r+"); // note to self: r+ is for a file that already exists
FILE *old_mfile = fopen("OLD.Makefile", "w+"); // w+ erases the file and starts in read-write mode with a fresh one
char line[200];
char *fn_ptr;
char *name = argv[1];
while(fgets(line, 199, mfile)) // first create the backup
{
fputs(line , old_mfile); // before changing the line, write it to the backup
memset(line,0x00,200);
}
rewind(mfile); // reset the files to position 0
rewind(old_mfile);
memset(line,0x00,200);
puts("Makefile backed-up as 'OLD.Makefile'");
fclose(mfile);
mfile = fopen("Makefile", "w");
while(fgets(line, 199, old_mfile)) // now lets loop again and rewrite with the new FNAME
{
if((fn_ptr = strstr(line, "FNAME=")) != NULL)
{
fn_ptr += strlen("FNAME=");
int i;
for(i = 0; i < strlen(name); i++)
{
*(fn_ptr+i) = *(name+i);
}
*(fn_ptr+i) = '\0';
}
// printf("%s", line); // for debugging
fputs(line , mfile);
fputs("\n" , mfile);
memset(line,0x00,200);
}
printf("FNAME is now: '%s'\n", argv[1]);
fclose(mfile);
fclose(old_mfile);
return 0;
}

Reading a file character by character in C

I'm writing a BF interpreter in C and I've run into a problem reading files. I used to use scanf in order to read the first string, but then you couldn't have spaces or comments in your BF code.
Right now here is what I have.
char *readFile(char *fileName)
{
FILE *file;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
{
*code++ = (char)fgetc(file);
} while(*code != EOF);
return code;
}
I know the problem arises in how I'm assigning the next char in the file to the code pointer but I'm just not sure what that is.
My pointer knowledge is lacking which is the point of this exercise.
The interpreter works fine, all using pointers, I'm just having a problem reading files in to it.
(I'm going to implement only reading +-><[]., into the file later, although if anyone has a good way to do it, it would be great if you'd let me know!)
There are a number of things wrong with your code:
char *readFile(char *fileName)
{
FILE *file;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
{
*code++ = (char)fgetc(file);
} while(*code != EOF);
return code;
}
What if the file is greater than 1,000 bytes?
You are increasing code each time you read a character, and you return code back to the caller (even though it is no longer pointing at the first byte of the memory block as it was returned by malloc).
You are casting the result of fgetc(file) to char. You need to check for EOF before casting the result to char.
It is important to maintain the original pointer returned by malloc so that you can free it later. If we disregard the file size, we can achieve this still with the following:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL)
return NULL; //could not open file
code = malloc(1000);
while ((c = fgetc(file)) != EOF)
{
code[n++] = (char) c;
}
// don't forget to terminate with the null character
code[n] = '\0';
return code;
}
There are various system calls that will give you the size of a file; a common one is stat.
Expanding upon the above code from #dreamlax
char *readFile(char *fileName) {
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL) return NULL; //could not open file
fseek(file, 0, SEEK_END);
long f_size = ftell(file);
fseek(file, 0, SEEK_SET);
code = malloc(f_size);
while ((c = fgetc(file)) != EOF) {
code[n++] = (char)c;
}
code[n] = '\0';
return code;
}
This gives you the length of the file, then proceeds to read it character by character.
Here's one simple way to ignore everything but valid brainfuck characters:
#define BF_VALID "+-><[].,"
if (strchr(BF_VALID, c))
code[n++] = c;
the file is being opened and not closed for each call to the function also
I think the most significant problem is that you're incrementing code as you read stuff in, and then returning the final value of code, i.e. you'll be returning a pointer to the end of the string. You probably want to make a copy of code before the loop, and return that instead.
Also, C strings need to be null-terminated. You need to make sure that you place a '\0' directly after the final character that you read in.
Note: You could just use fgets() to get the entire line in one hit.
Either of the two should do the trick -
char *readFile(char *fileName)
{
FILE *file;
char *code = malloc(1000 * sizeof(char));
char *p = code;
file = fopen(fileName, "r");
do
{
*p++ = (char)fgetc(file);
} while(*p != EOF);
*p = '\0';
return code;
}
char *readFile(char *fileName)
{
FILE *file;
int i = 0;
char *code = malloc(1000 * sizeof(char));
file = fopen(fileName, "r");
do
{
code[i++] = (char)fgetc(file);
} while(code[i-1] != EOF);
code[i] = '\0'
return code;
}
Like the other posters have pointed out, you need to ensure that the file size does not exceed 1000 characters. Also, remember to free the memory when you're done using it.
The problem here is twofold
a) you increment the pointer before you check the value read in, and
b) you ignore the fact that fgetc() returns an int instead of a char.
The first is easily fixed:
char *orig = code; // the beginning of the array
// ...
do {
*code = fgetc(file);
} while(*code++ != EOF);
*code = '\0'; // nul-terminate the string
return orig; // don't return a pointer to the end
The second problem is more subtle -fgetc returns an int so that the EOF value can be distinguished from any possible char value. Fixing this uses a temporary int for the EOF check and probably a regular while loop instead of do / while.

Resources