I have a stem file named myfile
, it has 2 sub files : myfile.data
and myfile.names
.
Now I want to write a code where it will first check the .names
file's last line and see whether there is a zero or not, if there is any other number other than zero then it will say noisy data
. And if the .names
files last line has a zero, then I need to read the .data file and do some operations on that .data
file to produce a valid output.
Now below I am giving what I have tried to do:
int main(int argc, char **argv)
{
char* path;
sprintf(path, "%s.names", argv[1]);
printf("...%s...\n", path);
//FILE *fp =stdin;
fp = fopen(path, "r");
char buff[1024];
int check = 0;
fseek(fp, 0, SEEK_SET);
while(!feof(fp))
{
memset(buff, 0x00, 1024);
fscanf(fp, "%[^\n]\n", buff);
}
if(strchr(buff, '0') != NULL){
check = 1;
}
if(check == 0){
printf("Noisy data...\n");
}
else if(check == 1){
char* path1;
sprintf(path1, "%s.data", argv[1]);
printf("...%s...", path1);
//FILE *fp1 = stdin;
fp1 = fopen(path1, "r");
char buf[1024];
if( fgets(buf, sizeof buf, fp1) == NULL ){
fputs("Input error\n", stderr);
return 1;
}
...
...
Produces the perfect output
}
It is compiling successfully.
To execute this : <./executable_filename> <stemFileName> > <outputFileName>
So whenever I am doing : ./myFileName myfile output
. It is saying Segmentation fault (core dumped)
. Why is it happening?
One more point my .names files will contain either : | Final Pseudo Deletion Count is 0.
or | Final Pseudo Deletion Count is 1.
or | Final Pseudo Deletion Count is 2.
, So I guess the strchr
will do my job is this case?
CodePudding user response:
Several bugs are immediately apparent:
char* path;
sprintf(path, "%s.names", argv[1]);
The buffer passed to sprintf
must point to writable memory, but path
is uninitialized and doesn't point anywhere in particular. See Crash or "segmentation fault" when data is copied/scanned/read to an uninitialized pointer. You probably want path
to be an array instead of a pointer.
Your compiler can warn you about many bugs like this, if you enable warnings, e.g. with gcc -Wall -O
. That would have saved you the time of asking this question (and me the time of answering it). Why should I always enable compiler warnings?
Also, since you cannot control the length of the string argv[1]
, no fixed length of the buffer can ever be long enough, so you probably want to use snprintf
instead to prevent a buffer overflow. See understanding the dangers of sprintf(...) (which is for C but addresses the same issue).
You have the same problems for path1
further down.
fp = fopen(path, "r");
fopen
will return NULL if it fails, and you never check for this. Your program would segfault in that case. Same problem with fp1
further down.
while(!feof(fp))
This is always wrong.
fscanf(fp, "%[^\n]\n", buff);
A similar overflow risk as with sprintf
. Also, you must check fscanf
for success; that is the proper way to check for end-of-file, in place of your erroneous while (!feof(fp))
.
if( fgets(buf, sizeof buf, fp1) == NULL ){
fputs("Input error\n", stderr);
return 1;
fgets
returning NULL does not necessarily indicate an "input error"; it could be simply that the end of the file has been reached.
For general information on causes of segmentation faults and how to debug them, see also
CodePudding user response:
Here's the initial code which is working completely fine when I don't want to check the .names
file's last line:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE * xfopen(const char *path, const char *mode);
void * xmalloc(size_t s);
void parse_line(const char *buf, int *max, int column_count)
{
for(int i = 0; i < column_count; i ){
char *end;
int t = strtol(buf, &end, 10);
if( t > max[i] ){
max[i] = t;
}
if( !((i < column_count - 1 && *end == ',') || (i == column_count - 1 && *end == '\n')) ){
fprintf(stderr, "invalid input '%c' in %s", *end, buf);
exit(1);
}
buf = end 1;
}
}
int main(int argc, char **argv)
{
char *path1;
char *path = argc > 1 ? argv[1] : "stdin";
sprintf(path, "%s.data", argv[1]);
FILE *in = argc > 1 ? xfopen(path, "r") : stdin;
char buf[1024];
int column_count = 1;
int row_count = 1;
int *max;
/* Read first line to determine number of columns */
if( fgets(buf, sizeof buf, in) == NULL ){
fputs("Input error\n", stderr);
return 1;
}
for( const char *p = buf; *p; p ){
if( *p == ',' ){
column_count = 1;
}
}
max = xmalloc(column_count * sizeof *max);
for( int i = 0; i < column_count; i ){
max[i] = INT_MIN;
}
parse_line(buf, max, column_count);
while( fgets(buf, sizeof buf, in) != NULL ){
row_count = 1;
parse_line(buf, max, column_count);
}
if( fseek(in, 0L, SEEK_SET) ){
perror(path);
return 1;
}
printf("%d %d ", row_count, column_count - 1);
for( int i = 0; i < column_count - 1; i = 1 ){
printf("%d ", max[i]);
}
printf("%d\n", max[column_count - 1] 1);
while( fgets(buf, sizeof buf, in) != NULL ){
char *comma = strrchr(buf, ',');
if( comma == NULL ){
fprintf(stderr, "Invalid input\n");
return 1;
}
*comma = '\0';
int k = strtol(comma 1, NULL, 10);
for(char *p = buf; *p; p ){
if( *p == ',' ) *p = ' ';
}
printf("%s %d\n", buf, k 1);
}
}
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 *
xmalloc(size_t s)
{
void *rv = malloc(s);
if( rv == NULL ){
perror("malloc");
exit(EXIT_FAILURE);
}
return rv;
}