Related
I wrote a program, which reads from a file. I use a condition in which I print that the array is too big, but when I use a too big array instead of showing this message I have segmentation fault.
This is my program
#include <stdio.h>
#include <stdlib.h>
#define N 10000 // Maximum array size
int _strlen(char *array) {
int i;
for (i = 0; array[i] != '\0'; ++i);
return i;
}
int readText(FILE *wp, char *s, int max) {
int sum = 0;
if (_strlen(s) > max) {
printf("This array is too big. Maximum size is %d", max);
} else {
while ((*s++ = fgetc(wp)) != EOF) {
sum++;
}
*(s-1) = '\0';
}
return sum;
}
int main(int argc, char *argv[]) {
FILE *wz, *wc;
char *s;
char array[N];
s = array;
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n",argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
fprintf(wc, "Read text from file source.txt");
readText(wz, s, 10000);
return 0;
}
In output I want to have: This array is too big. Maximum size is %d
Instead of Segmentation fault core dumped
In addition, I want to say that the program is when I use a smaller array, but I want to show the user a proper message when he uses too big array instead of segmentation fault.
Thanks, I change my program in that way. The only problem is that this program check the if condition in every while loop so this program could be slow.
int readText(FILE *wp, char *s, int max) {
int sum = 0;
if (_strlen(s) > max) {
printf("This array is too big. Maximum size is %d", max);
} else {
while ((*s++ = fgetc(wp)) != EOF) {
sum++;
if (sum > max) {
printf("This array is too big. Maximum size is %d", max);
break;
}
}
*(s-1) = '\0';
}
return sum;
}
The remarks / other answer solve your undefined behavior (segmentation fault in your case).
The only problem is that this program check the if condition in every while loop so this program could be slow.
Your program is not slow because of a 'if' but because you read the file char per char.
Using stat or equivalent function you can get the size of the file to read it throw only one fread :
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#define N 10000 // Maximum array size
int main(int argc, char *argv[]) {
char array[N];
FILE *wz, *wc;
struct stat st;
off_t sz;
if (argc != 3) {
printf("Wrong arguments number\n"
"I should run this way:\n"
"%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
exit(1);
}
if (stat(argv[1], &st) == -1) {
printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
exit(1);
}
if (st.st_size > N-1) {
printf("This array is too big. Maximum size is %d", N-1);
sz = N-1;
}
else
sz = st.st_size;
if (fread(array, 1, sz, wz) != sz) {
printf("cannot read %s : %s", argv[1], strerror(errno));
fclose(wz); /* for valgrind end test etc */
exit(1);
}
array[sz] = 0;
fclose(wz);
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
fclose(wz); /* for valgrind end test etc */
exit(2);
}
/* ... */
fclose(wc);
return 0;
}
Knowing the size of the file allows to remove that limitation to a constant size and try to read the file while you can allocate enough memory for :
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
char * array;
FILE *wz, *wc;
struct stat st;
if (argc != 3) {
printf("Wrong arguments number\n"
"I should run this way:\n"
"%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
exit(1);
}
if (stat(argv[1], &st) == -1) {
printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
exit(2);
}
if ((array = malloc(st.st_size + 1)) == NULL) {
printf("Not enough memory to memorize the file %s\n", argv[1]);
exit(3);
}
if (fread(array, 1, st.st_size, wz) != st.st_size) {
printf("cannot read %s : %s", argv[1], strerror(errno));
fclose(wz); /* for valgrind end test etc */
free(array); /* for valgrind etc */
exit(4);
}
array[st.st_size] = 0;
fclose(wz);
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
free(array); /* for valgrind etc */
exit(5);
}
/* ... */
fclose(wc);
free(array); /* for valgrind etc */
return 0;
}
Anyway because of the usage of the program "source result" may be you want to copy the file specified by argv[1] in the file specified by argv[2], in that case better to read and write block by block rather than to read all to not use a lot of memory for nothing and to manage the case the input file size is greater than the memory size.
You cannot measure the length of the destination array with _strlen(s), the size is given as an argument and reading an uninitialized array with _strlen() has undefined behavior.
Furthermore, you store fgetc(fp) to *s++ before testing for EOF. This is incorrect in all cases:
if char type is signed, EOF cannot be distinguished from a valid byte value of \377.
if char is unsigned, EOF cannot be tested because it has been converted as a char value of 0xff, hence the loop runs forever, writing beyond the end of the destination array until this causes a crash.
You simply want to add a test in the reading loop to stop reading bytes from the file when the buffer is full and read the bytes into an int variable so you can test for end of file reliably.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#define N 10000 // Maximum array size
int readText(FILE *wp, char *s, int max) {
int i = 0, c;
while (i < max - 1 && (c = fgetc(wp)) != EOF) {
s[i++] = c;
}
s[i] = '\0';
return i;
}
int main(int argc, char *argv[]) {
FILE *wz, *wc;
char array[N];
int nread;
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
fprintf(wc, "Read text from file source.txt\n");
nread = readText(wz, array, N);
printf("Read %d bytes\n", nread);
return 0;
}
I am curious what is the best way to open multiple files. I know you use a combination of FILE *inputfp1; and inputfp1 = fopen(argv[1], "r"); then check for errors. I would like to know the best way to do this.
Is it best to open and close one file at a time like this?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char **argv) {
char line[80] = {0};
FILE *inputfp1;
//input = fopen("myfile.txt", "r");
inputfp1 = fopen(argv[1], "r"); //Open file for read.
if (inputfp1 == NULL)
{
printf("Error opening file %s!",argv[1]); //Program prints error message and closes if file is not found
exit(0);
}
if( argc == 7 )
{
/*printf("The first argument supplied is %s\n", argv[1]);
printf("The second argument supplied is %s\n", argv[2]);
printf("The third argument supplied is %s\n", argv[3]);
printf("The first argument supplied is %s\n", argv[4]);
printf("The second argument supplied is %s\n", argv[5]);
printf("The third argument supplied is %s\n", argv[6]);
printf("The third argument supplied is %s\n", argv[7]);*/
}
else if( argc > 7 )
{
printf("Too many arguments supplied.\n");
exit( 1 );
}
else
{
printf("Not enough arguments supplied. \n");
exit( 1 );
}
//Unique behavior on file1
while(fgets(line, 80, inputfp1) != NULL)
{
//do work on file1
}
fclose(inputfp1);
inputfp1 = fopen(argv[2], "r"); //Open file for read.
if (inputfp1 == NULL)
{
printf("Error opening file %s!",argv[1]); //Program prints error message and closes if file is not found
exit(0);
}
//Unique behavior on file2
while(fgets(line, 80, inputfp1) != NULL)
{
//do work on file2
}
fclose(inputfp1);
return 0;
}
Is it better to create all the file pointers and open all the files at once like this?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char **argv) {
char line[80] = {0};
FILE *inputfp1;
FILE *inputfp2;
FILE *inputfp3;
FILE *inputfp4;
FILE *inputfp5;
FILE *inputfp6;
//input = fopen("myfile.txt", "r");
inputfp1 = fopen(argv[1], "r"); //Open file for read.
inputfp2 = fopen(argv[2], "r"); //Open file for read.
inputfp3 = fopen(argv[3], "r"); //Open file for read.
inputfp4 = fopen(argv[4], "r"); //Open file for read.
inputfp5 = fopen(argv[5], "r"); //Open file for read.
inputfp6 = fopen(argv[6], "r"); //Open file for read.
if (inputfp1 == NULL)
{
printf("Error opening file %s!",argv[1]); //Program prints error message and closes if file is not found
exit(0);
}
//The rest of error checking.
if( argc == 7 )
{
/*printf("The first argument supplied is %s\n", argv[1]);
printf("The second argument supplied is %s\n", argv[2]);
printf("The third argument supplied is %s\n", argv[3]);
printf("The first argument supplied is %s\n", argv[4]);
printf("The second argument supplied is %s\n", argv[5]);
printf("The third argument supplied is %s\n", argv[6]);
printf("The third argument supplied is %s\n", argv[7]);*/
}
else if( argc > 7 )
{
printf("Too many arguments supplied.\n");
exit( 1 );
}
else
{
printf("Not enough arguments supplied. \n");
exit( 1 );
}
//Unique behavior on file1
while(fgets(line, 80, inputfp1) != NULL)
{
//do work on file1
}
fclose(inputfp1);
//Unique behavior on file2
while(fgets(line, 80, inputfp2) != NULL)
{
//do work on file2
}
fclose(inputfp2);
//The rest of reading and closing files.
return 0;
}
Are there any better ways I missed?
A good way of doing this would be putting all your file pointers in an array:
FILE *inputfp[6];
for(int i=0;i<6;i++)
{
inputfp[i] = fopen(argv[i+1], "r"); //Open file for read.
if (inputfp[i] == NULL)
{
printf("Error opening file %s!",argv[i+1]); //Program prints error message and closes if file is not found
exit(0);
}
}
I'd do it the first way, but use a loop:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char **argv) {
char line[80] = {0};
FILE *inputfp1;
if( argc == 7 )
{
/*printf("The first argument supplied is %s\n", argv[1]);
printf("The second argument supplied is %s\n", argv[2]);
printf("The third argument supplied is %s\n", argv[3]);
printf("The first argument supplied is %s\n", argv[4]);
printf("The second argument supplied is %s\n", argv[5]);
printf("The third argument supplied is %s\n", argv[6]);
printf("The third argument supplied is %s\n", argv[7]);*/
}
else if( argc > 7 )
{
printf("Too many arguments supplied.\n");
exit( 1 );
}
else
{
printf("Not enough arguments supplied. \n");
exit( 1 );
}
for (int i = 1; i < argc; ++i)
{
inputfp1 = fopen(argv[i], "r"); //Open file for read.
if (inputfp1 == NULL)
{
printf("Error opening file %s!",argv[i]); //Program prints error message and closes if file is not found
exit(0);
}
while(fgets(line, 80, inputfp1) != NULL)
{
//do work
}
fclose(inputfp1);
}
return 0;
}
I can't figure out why this isn't working.
#include <stdio.h>
int main(void) {
FILE *in, *out;
// char *FULLPATH = "C:\\Users\\Jay\\c\\workspace\\I-OFiles\\in.txt\\ ";
// char *mode = "r";
// in = fopen(FULLPATH, mode);
//
// if (in == NULL) {
// perror("Can't open in file for some reason\n");
// exit (1);
// }
out = fopen("C:\\Users\\Jay\\c\\workspace\\I-OFiles\\out.txt", "w");
if (out == NULL) {
perror("Can't open output file for some reason \n");
exit(1);
}
fprintf(out, "foo U");
fclose(in);
fclose(out);
return 0;
}
if I remove the // from the commented lines, the error compiler gives is
: Invalid argument
I don't understand why (I read all the other threads related, and nothing).
It does actually write the out.txt file OK, so it doesn't seem like a path misspelled problem.
Remove backslash after in.txt.
The input file name seems bogus:
"C:\\Users\\Jay\\c\\workspace\\I-OFiles\\in.txt\\ "
The filename is just a single space " " and in.txt is probably not a directory.
Change the code to:
const char *FULLPATH = "C:\\Users\\Jay\\c\\workspace\\I-OFiles\\in.txt";
Or preferably:
const char *FULLPATH = "C:/Users/Jay/c/workspace/I-OFiles/in.txt";
for better portability as forward slashes work in Windows as well as in Unix.
Furthermore, it is easy to provide more information as to why fopen() failed to open the files.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
FILE *in, *out;
in = fopen("C:/Users/Jay/c/workspace/I-OFiles/in.txt", "r");
if (in == NULL) {
perror("Cannot open input file");
exit(1);
}
out = fopen("C:/Users/Jay/c/workspace/I-OFiles/out.txt", "w");
if (out == NULL) {
fclose(in);
perror("Cannot open output file");
exit(1);
}
fprintf(out, "foo U");
fclose(in);
fclose(out);
return 0;
}
Change backslash to slash.
Maybe you don't have permissions or something like that.
out = fopen("C://Users//Jay//c//workspace//I-OFiles//out.txt", "w");
if (!out)
perror("fopen");
return 0;
I need to open a file located on Desktop(Linux). If i write the location as a string inside the fopen() function it works, but if i pass it as a variable, it doesn't work. Here is my code :
fp = fopen(readPathToFile, "r");
if (!fp){
printf("Failed to open text file\n");
exit(1);
}
else{
fscanf(fp,"%s",line);
printf("File read: %s",line);
}
If i write it like this, it shows me the content of file :
fp = fopen("home/user/Desktop/test.txt", "r");
if (!fp){
printf("Failed to open text file\n");
exit(1);
}
else{
fscanf(fp,"%s",line);
printf("File read: %s",line);
}
The child process opens the file. Here is my full code
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#define READ 0
#define WRITE 1
int main ()
{
pid_t pid;
int mypipefd[2];
id_t child_pid;
char line[100];
char *pathToFile[100];
FILE *fp;
char buff[255];
/* create the pipe */
if (pipe(mypipefd) == -1) {
fprintf(stderr,"Pipe failed");
return 1;
}
child_pid = fork () ;
if (child_pid > 0) {
printf("Introduceti locatia catre fisier:");
fgets(pathToFile, 100, stdin);
close(mypipefd[READ]);
write(mypipefd[WRITE], &pathToFile, sizeof(pathToFile));
close(mypipefd[WRITE]);
printf("parent: write value : %s",pathToFile);
}
else if (child_pid < 0) {
fprintf(stderr, "Fork failed");
return 1;
}
else{
char *readPathToFile[100];
close(mypipefd[WRITE]);
read(mypipefd[READ], &readPathToFile, sizeof(readPathToFile));
close(mypipefd[READ]);
printf("child: read value : %s",readPathToFile);
fp = fopen(readPathToFile, "r");
if (!fp)
{
printf("Failed to open text file\n");
exit(1);
}
else{
fscanf(fp,"%s",line);
printf("File read: %s",line);
}
}
return 0;
}
Your compiler did not warn you about the type mismatch in
char *pathToFile[100];
fgets(pathToFile, 100, stdin);
(array of 100 pointers-to-char versus array of 100 chars)? Did you turn warnings off?
Also note that fgets retains the newline. Your file name probably does not end with a newline. You should replace it with a NUL (zero) byte.
Typically you don't need a debugger to track these down. A little bit of printf debugging can do wonders. :-)
Okay, so this is the root of your problem:
char *pathToFile[100];
This declares pathToFile as a 100-element array of pointers to char, not a 100-element array of char. The first thing you need to do is change that declaration to
char pathToFile[100];
Secondly, fgets will save the trailing newline from your input to the target buffer if there's room, so you'll need to remove that newline from the input:
char *newline = strchr( pathToFile, '\n' );
if ( newline )
*newline = 0;
I'm currently writing a chat program in C. It operates in two terminals, and uses one program. A sample input of session 1 would look like:
./a.out a.txt b.txt Phil
Where a.txt is the file to be used for input/reading, and b.txt is the file to be used for output/sending a message. Phil is the chat handle.
It follows that the input of session 2 looks like:
./a.out b.txt a.txt Jill
So that the input file is the output file of the first session, enabling the chat to work, and for the two terminals to talk to each other.
A sample run of this program would look something like:
Send: Are you there?
Received [Jill]: Yup!
Send: Okay see ya!
Received [Jill]: Bye!
^C
And vice versa in the other terminal. However, I'm having trouble getting my program to send the files and receive them automatically. My output looks correct, but I can only receive a message if I send one, which sort of defeats the purpose of the chat. My question is, where in my while loop am I going wrong, to where I have to send a message before I can read the one the other session has sent? Here is the code I have so far:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[]) {
// Initialize file variables
FILE *in;
FILE *out;
// Initialize incoming, incoming check, and outgoing variables
char inc[300] = " ";
char incCheck[300] = " ";
char send[300];
char handle[300];
int size, counter;
counter=1;
// This checks if a user has already entered a message
// i.e. entered the chat
if (in = fopen(argv[1], "r")) {
fclose(in);
}
else {
// create an empty in.txt to bypass segfault if the file doesn't already exist
in = fopen(argv[1], "w");
fclose(in);
// To check if anything has been received yet.
if (strcmp(inc, incCheck) == 0) {
printf("Nothing has been received yet.\n");
}
}
// The while loop that reads and writes the files
while(1) {
// Read the incoming file and put it to a string
// It will read the incoming file over and over until a message is seen
in = fopen(argv[1], "r");
fgets(inc, size, in);
fclose(in);
// This counter is only for the first message, since it is not possible
// that one has already been received.
if (counter > 0) {
size=sizeof(send);
printf("Send: \t");
fgets(send, size, stdin);
out = fopen(argv[2], "w");
fprintf(out, "%s",send);
fclose(out);
counter=0;
}
// If the incoming file is different, print it out
if (strcmp(inc, incCheck) != 0) {
printf("Received [%s]: %s", argv[3], inc);
in = fopen(argv[1], "r");
fgets(inc, size, in);
strcpy(incCheck, inc);
fclose(in);
// And prompt to send another message.
size=sizeof(send);
printf("Send: \t");
fgets(send, size, stdin);
out = fopen(argv[2], "w");
fprintf(out, "%s",send);
fclose(out);
in = fopen(argv[1], "r");
fgets(inc, size, in);
fclose(in);
}
}
Setting one side of the chat as initiator solves the problem. Is that sufficient ? In code below this is solved by setting one of the handles as "s".
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[]) {
// Initialize file variables
FILE *in;
FILE *out;
// Initialize incoming, incoming check, and outgoing variables
char inc[300] = " ";
char incCheck[300] = " ";
char send[300];
char oldsend[300];
char handle[300];
int size, counter;
counter=1;
// This checks if a user has already entered a message
// i.e. entered the chat
if (in = fopen(argv[1], "r")) {
fclose(in);
}
else {
// create an empty in.txt to bypass segfault if the file doesn't already exist
in = fopen(argv[1], "w");
fclose(in);
// To check if anything has been received yet.
if (strcmp(inc, incCheck) == 0) {
printf("Nothing has been received yet.\n");
}
}
in = fopen(argv[1], "r");
fgets(incCheck, size, in);
fclose(in);
in = fopen(argv[2], "r");
fgets(oldsend, size, in);
fclose(in);
// The while loop that reads and writes the files
while(1) {
// Read the incoming file and put it to a string
// It will read the incoming file over and over until a message is seen
// This counter is only for the first message, since it is not possible
// that one has already been received.
if ((counter > 0) && ( strcmp(argv[3],"s")==0)) {
size=sizeof(send);
do {
printf("Send: \t");
fgets(send, size, stdin);
}while (strcmp(send, oldsend)==0);
strcpy (oldsend,send);
out = fopen(argv[2], "w");
fprintf(out, "%s",send);
fclose(out);
counter=0;
}
in = fopen(argv[1], "r");
fgets(inc, size, in);
fclose(in);
}
// If the incoming file is different, print it out
if (strcmp(inc, incCheck) != 0) {
printf("Received [%s]: %s", argv[3], inc);
in = fopen(argv[1], "r");
fgets(inc, size, in);
strcpy(incCheck, inc);
fclose(in);
// And prompt to send another message.
size=sizeof(send);
do {
printf("Answer: \t");
fgets(send, size, stdin);
} while (strcmp(send, oldsend)==0);
strcpy (oldsend,send);
out = fopen(argv[2], "w");
fprintf(out, "%s",send);
fclose(out);
}
}
return 0;
}