-1

I'm working through the ACM code challenges, and I need to read from a file, which the user has specified. The code I have thus far:


int main(){

    // Input file name

    char filename[80];
    printf("\nInput file name: ");
    fgets(filename, 79, stdin);

    // Read from file

    FILE *data_file;
    data_file = fopen(filename,"r");

    char data[1000];

    for(int i=0; i<1000; i++){
        fscanf(data_file, "%1d", &data[i]);
    }

    fclose(data_file);

    return 0;
}

However, there's an error: 'segmentation fault.' Using some printf statements, I know the filename was read properly.


Running Ubuntu 14.04 LTS; the IDE is Code::Blocks.

user1997744
  • 411
  • 4
  • 16
  • 3
    If you want to keep using C in the long run, I highly recommend learning to use a debugger. For starters, they will tell you the line of the segmentation fault without needing to do trial and error with printfs. – hugomg Jun 26 '14 at 22:00
  • 3
    Not checking the return value of `fopen` is a serious error in this case. – Daniel Kamil Kozar Jun 26 '14 at 22:02
  • Can you come up with a small input file that shows your problem? Have you tried creating a smaller version of your program that still has the error (for example, consider hardcoding the input file name instead of reading from stdin) – hugomg Jun 26 '14 at 22:04
  • @hugomg: Yes, created a test file ("test.txt"), and wrote the file name directly in the fopen function, but still got the error. – user1997744 Jun 26 '14 at 22:06
  • It's the newline. I bet it works fine if the filename is exactly 79 characters long. You can use `gets` instead if it's throwaway code; otherwise, trim the newline yourself. – tmyklebu Jun 26 '14 at 22:28
  • `"%1d"` need `int*` but give `char*`. – BLUEPIXY Jun 26 '14 at 22:28

3 Answers3

3

You have a number of errors here. The most important being that you aren't checking the result of fopen for success, which means you will attempt to call fscanf on an invalid file handle, resulting in your segfault issue.

Another is that you are not trimming the trailing newline \n character that fgets leaves just before the trailing NULL character in the filename buffer. This means that you will almost certainly never get past this line successfully.

Third, your fscanf call isn't being check for error conditions (ie: End-Of-File), so it will attempt to read out of bounds and also introduce errors.

Finally, there are a lot of hard-coded sentinel values used in your code. You should replace those with either macros, or const int values if your compiler supports VLAs and the C99 standard.

Sample Code


#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#ifndef PATH_MAX
#define  PATH_MAX    (1024)
#endif

#define BUF_LEN_MAX  (1024)

int main() {
    /* Declare variables */
    int i;
    bool err = false;
    char filename[PATH_MAX];
    char data[BUF_LEN_MAX];
    FILE *data_file;

    /* Get input file name */
    printf("\nInput file name: ");
    fgets(filename, PATH_MAX, stdin);
    strtok(filename, "\n");   /* Trim trailing newline character left behind by fgets */
    printf("Data file to use:%s\n", filename);

    // Read from file
    if ((data_file = fopen(filename,"r")) == NULL) {
       printf("File could not be found or opened; Aborting\n");
       return 1;
    }
    /* Print out the gathered data one character at a time for verification */
    for (i=0; i<PATH_MAX && !err; i++) {
       if (fscanf(data_file, "%c", &data[i]) == EOF) {
          err = true;
       }
       printf("Character #%04d - '%c'\n", i, data[i]);
    }
    fclose(data_file);

    return 0;
}

Sample Test File - tmp.txt


total 32
drwxr-xr-x   4 admin   darkstar   136B Jun 26 15:00 ./
drwxr-xr-x@ 62 admin   darkstar   2.1K Jun 26 13:38 ../
-rw-r--r--@  1 admin   darkstar    12K May 13 05:52 .DS_Store
-rw-r--r--   1 admin   darkstar     0B Jun 26 15:00 tmp.txt

Sample Run - Intentional Failure Test


Input file name: someone
Data file to use:someone
File could not be found or opened; Aborting

Sample Run - Test 2 - Success


Input file name: tmp.txt
Data file to use:tmp.txt
Character #0000 - 't'
Character #0001 - 'o'
Character #0002 - 't'
Character #0003 - 'a'
Character #0004 - 'l'
Character #0005 - ' '
Character #0006 - '3'
Character #0007 - '2'
Character #0008 - '
'
Character #0009 - 'd'
Character #0010 - 'r'
Character #0011 - 'w'
Character #0012 - 'x'
Character #0013 - 'r'
Character #0014 - '-'
Character #0015 - 'x'
Character #0016 - 'r'
Character #0017 - '-'
Character #0018 - 'x'
Character #0019 - ' '
Character #0020 - ' '
Character #0021 - ' '
Character #0022 - '4'
Character #0023 - ' '
Character #0024 - 'a'
Character #0025 - 'd'
Character #0026 - 'm'
Character #0027 - 'i'
Character #0028 - 'n'
Character #0029 - ' '
Character #0030 - ' '
Character #0031 - ' '
Character #0032 - 'd'
Character #0033 - 'a'
Character #0034 - 'r'
Character #0035 - 'k'
Character #0036 - 's'
Character #0037 - 't'
Character #0038 - 'a'
Character #0039 - 'r'
Character #0040 - ' '
Character #0041 - ' '
Character #0042 - ' '
Character #0043 - '1'
Character #0044 - '3'
Character #0045 - '6'
Character #0046 - 'B'
Character #0047 - ' '
Character #0048 - 'J'
Character #0049 - 'u'
Character #0050 - 'n'
Character #0051 - ' '
Character #0052 - '2'
Character #0053 - '6'
Character #0054 - ' '
Character #0055 - '1'
Character #0056 - '5'
Character #0057 - ':'
Character #0058 - '0'
Character #0059 - '0'
Character #0060 - ' '
Character #0061 - '.'
Character #0062 - '/'
Character #0063 - '
'
Character #0064 - 'd'
Character #0065 - 'r'
Character #0066 - 'w'
Character #0067 - 'x'
Character #0068 - 'r'
Character #0069 - '-'
Character #0070 - 'x'
Character #0071 - 'r'
Character #0072 - '-'
Character #0073 - 'x'
Character #0074 - '@'
Character #0075 - ' '
Character #0076 - '6'
Character #0077 - '2'
Character #0078 - ' '
Character #0079 - 'a'
Character #0080 - 'd'
Character #0081 - 'm'
Character #0082 - 'i'
Character #0083 - 'n'
Character #0084 - ' '
Character #0085 - ' '
Character #0086 - ' '
Character #0087 - 'd'
Character #0088 - 'a'
Character #0089 - 'r'
Character #0090 - 'k'
Character #0091 - 's'
Character #0092 - 't'
Character #0093 - 'a'
Character #0094 - 'r'
Character #0095 - ' '
Character #0096 - ' '
Character #0097 - ' '
Character #0098 - '2'
Character #0099 - '.'
Character #0100 - '1'
Character #0101 - 'K'
Character #0102 - ' '
Character #0103 - 'J'
Character #0104 - 'u'
Character #0105 - 'n'
Character #0106 - ' '
Character #0107 - '2'
Character #0108 - '6'
Character #0109 - ' '
Character #0110 - '1'
Character #0111 - '3'
Character #0112 - ':'
Character #0113 - '3'
Character #0114 - '8'
Character #0115 - ' '
Character #0116 - '.'
Character #0117 - '.'
Character #0118 - '/'
Character #0119 - '
'
Character #0120 - '-'
Character #0121 - 'r'
Character #0122 - 'w'
Character #0123 - '-'
Character #0124 - 'r'
Character #0125 - '-'
Character #0126 - '-'
Character #0127 - 'r'
Character #0128 - '-'
Character #0129 - '-'
Character #0130 - '@'
Character #0131 - ' '
Character #0132 - ' '
Character #0133 - '1'
Character #0134 - ' '
Character #0135 - 'a'
Character #0136 - 'd'
Character #0137 - 'm'
Character #0138 - 'i'
Character #0139 - 'n'
Character #0140 - ' '
Character #0141 - ' '
Character #0142 - ' '
Character #0143 - 'd'
Character #0144 - 'a'
Character #0145 - 'r'
Character #0146 - 'k'
Character #0147 - 's'
Character #0148 - 't'
Character #0149 - 'a'
Character #0150 - 'r'
Character #0151 - ' '
Character #0152 - ' '
Character #0153 - ' '
Character #0154 - ' '
Character #0155 - '1'
Character #0156 - '2'
Character #0157 - 'K'
Character #0158 - ' '
Character #0159 - 'M'
Character #0160 - 'a'
Character #0161 - 'y'
Character #0162 - ' '
Character #0163 - '1'
Character #0164 - '3'
Character #0165 - ' '
Character #0166 - '0'
Character #0167 - '5'
Character #0168 - ':'
Character #0169 - '5'
Character #0170 - '2'
Character #0171 - ' '
Character #0172 - '.'
Character #0173 - 'D'
Character #0174 - 'S'
Character #0175 - '_'
Character #0176 - 'S'
Character #0177 - 't'
Character #0178 - 'o'
Character #0179 - 'r'
Character #0180 - 'e'
Character #0181 - '
'
Character #0182 - '-'
Character #0183 - 'r'
Character #0184 - 'w'
Character #0185 - '-'
Character #0186 - 'r'
Character #0187 - '-'
Character #0188 - '-'
Character #0189 - 'r'
Character #0190 - '-'
Character #0191 - '-'
Character #0192 - ' '
Character #0193 - ' '
Character #0194 - ' '
Character #0195 - '1'
Character #0196 - ' '
Character #0197 - 'a'
Character #0198 - 'd'
Character #0199 - 'm'
Character #0200 - 'i'
Character #0201 - 'n'
Character #0202 - ' '
Character #0203 - ' '
Character #0204 - ' '
Character #0205 - 'd'
Character #0206 - 'a'
Character #0207 - 'r'
Character #0208 - 'k'
Character #0209 - 's'
Character #0210 - 't'
Character #0211 - 'a'
Character #0212 - 'r'
Character #0213 - ' '
Character #0214 - ' '
Character #0215 - ' '
Character #0216 - ' '
Character #0217 - ' '
Character #0218 - '0'
Character #0219 - 'B'
Character #0220 - ' '
Character #0221 - 'J'
Character #0222 - 'u'
Character #0223 - 'n'
Character #0224 - ' '
Character #0225 - '2'
Character #0226 - '6'
Character #0227 - ' '
Character #0228 - '1'
Character #0229 - '5'
Character #0230 - ':'
Character #0231 - '0'
Character #0232 - '0'
Character #0233 - ' '
Character #0234 - 't'
Character #0235 - 'm'
Character #0236 - 'p'
Character #0237 - '.'
Character #0238 - 't'
Character #0239 - 'x'
Character #0240 - 't'
Character #0241 - '
'
Character #0242 - ''

References


  1. Removing trailing newline character from fgets() input, Accessed 2014-06-27, <https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input>
Community
  • 1
  • 1
Cloud
  • 18,753
  • 15
  • 79
  • 153
  • I copied the code, ran it, and tried inputting a text file, "test.txt" but it did not work. I made the file in shell, in the same directory as the executable, like so: echo "text" > test.txt. – user1997744 Jun 27 '14 at 07:58
  • Fixed the issue; had to enter the entire file path :) – user1997744 Jun 27 '14 at 08:33
1

The string: filename, after it is populated has a 0x0A before the NULL:

enter image description here

Or, shown another way:

enter image description here

This results in a file not found error at fopen(). At this point, your code should exit. Going beyond this point will result in undefined behavior.

This may not be your only error, but to remedy this error only, you can do something like:

(after populating filename from stdin) you have to clean it up a little...

int len = strlen(filename);
filename[len-2]=0;  

Additional error is in this section of code:

char data[1000];  //You have created char data type

for(int i=0; i<1000; i++){
    fscanf(data_file, "%1d", &data[i]);//but your format string "%1d" expects an int.
}

Either Change fscanf(data_file, "%1d", &data[i]);
To fscanf(data_file, "%s", data[i]);

Or create an int array:

int data[1000];

And use the original fscanf() call.

By the way, you should be checking the output values of fopen() & fscanf() for expected results and handle exceptions if they occur.

ryyker
  • 22,849
  • 3
  • 43
  • 87
  • I tried just typing fopen("test.txt","r"), and the error still occurs. Also tried your solution, with generic filename. – user1997744 Jun 26 '14 at 22:15
  • @user1997744 You need to type in the path also... eg. On my system, I had to type in c:\dev\a.txt to get it to be close. _BUT_ - That results in the extra carriage return I pointed out in my answer. – ryyker Jun 26 '14 at 22:26
  • Just for the record, I'm not the downvoter. I thought that if the file was in the same directory as the executable, you didn't need to specify the full path? – user1997744 Jun 27 '14 at 07:26
  • @user1997744 - You are correct, using `fopen()`you do not need to specify any path information for local dir. I usually write something like ".\filename.txt" to be specific, but it is not necessary. The problem you were having, and by now hopefully solved is the extra 0x0A (carriage return) imbedded between the last readable character and the NULL terminator. All three answers have addressed how to handle that. – ryyker Jun 27 '14 at 17:40
0

You should be writing something like this. You always need to check if the file can be opened. It would be better to just pass in the path on the commandline and pull it out of argv.

int main(){
char filename[80];
FILE *data_file;
char data[1000];
int i;

/* Input file name */
printf("\nInput file name: ");
fgets(filename, 79, stdin);

/* Remove '/n' from filename string*/
if (filename && strlen(filename) > 1) {
   filename[strlen(filename)-2] = 0;
 }

/* Open file */
data_file = fopen(filename,"r");

/* Read data */
if (data_file) {
   for (i=0; i<1000; i++){
      fscanf(data_file, "%1d", &data[i]);
      }
  fclose(data_file);
  }
else {
    printf("Could not open file\n");
    }
return 0;
}
dantill
  • 29
  • 2