Why does it matter where fopen is used? - c

I could use some help understanding something puzzling to me. It concerns the position of of fopen() to read out a file.
Following code (C compiled with gcc 4.5.2):
#include <stdlib.h>
#include <stdio.h>
void try_fopen(FILE* f_handle, const char* f_name, const char* mode) {
f_handle = fopen(f_name, mode);
if( f_handle == NULL ) {
fprintf(stderr, "Error: Unable to open '%s'.", f_name);
exit(EXIT_FAILURE);
}
}
int cnt_ones(FILE* pFile) {
int c;
int n = 0;
do {
c = fgetc (pFile);
if (c == '1') n++;
} while (c != EOF);
return n;
}
Why is it that putting the fopen in a function gives a Segfault:
int main (int argc, char** argv) {
FILE * pFile;
try_fopen(pFile, argv[1], "r"); // Gives a Segfault
printf ("The file contains %d ones.\n", cnt_ones(pFile) );
fclose (pFile);
return 0;
}
While putting it into the main (along with the if doesn't):
int main (int argc, char** argv) {
FILE * pFile;
pFile = fopen(argv[1], "r"); // While this doesn't give a Segfault
if( pFile == NULL ) {
fprintf(stderr, "Error: Unable to open '%s'.", argv[1]);
exit(EXIT_FAILURE);
}
printf ("The file contains %d sign characters.\n", cnt_ones(pFile) );
fclose (pFile);
return 0;
}

C is pass by value, not by reference, so you need to pass the pointer to pFile, otherwise you don't change it outside of the function scope:
void try_fopen(FILE** f_handle, const char* f_name, const char* mode) {
*f_handle = fopen(f_name, mode);
if( *f_handle == NULL ) {
fprintf(stderr, "Error: Unable to open '%s'.", f_name);
exit(EXIT_FAILURE);
}
}
// ...
try_fopen(&pFile, argv[1], "r");

Because the pointer pFile is passed by value to the function try_open. The value modified inside the function is not available in the main. To solve this, you need to pass the address of the pointer to the function, so try_open would accept FILE** and assign the result of fopen to *pFile. While calling this function you should pass the address of pFile using &pFile.

You can either do :
File * fp;
try_fopen( &fp,.....); /* void try_fopen (FILE ** fp,....) */
or the following :
File * fp = try_fopen("file name"); /* FILE * try_fopen (const char * file_name,...) */

The reason is simple, when you pass FILE* to function, it's updation will be lost as it is passed by Value. Try passing FILE ** to the function and it will work. Refer to Binyamin Sharet's answer above for the code snippet
The reason for this can be understood by reading this link
Or
You can change the function try_open to return FILE * as the return value.
FILE *try_fopen(const char* f_name, const char* mode)
{
FILE *f_handle = NULL;
*f_handle = fopen(f_name, mode);
if( *f_handle == NULL )
{
fprintf(stderr, "Error: Unable to open '%s'.", f_name);
exit(0);
}
}
//In the main function.
FILE *pFile = try_fopen(argv[1], "r");

Related

Arguments to open file

I want to open a file using the arguments when executing it, for example:
./Project 123.txt
I can make this work to some extent, but if I try to pass the arguments into a function, and then call the function, something is not working quite right.
Here is the function I have, which will read the lines from a text file:
int numeroLinhas(){
int ch;
FILE *fp;
int linhas=0;
fp = fopen("123.txt","r");
while( ( ch = fgetc(fp) ) != EOF ){
if(ch=='\n'){
linhas++;
}
}
fclose(fp);
fprintf(stats, "linhas: %d\n", linhas);
}
int main(int argc, char *argv[]){
FILE *fp;
fp = fopen(argv[1],"r");
numeroLinhas();
}
So, I would like to know how can I pass the argv[] has an argument into my numeroLinhas() function, so that I don't have to call the name of the file at all during the coding, just when executing.
The problem is that you have hard-coded the file name to open. Modify your function to take an argument, a string naming the file.
Then just pass the correct argv entry as the argument to the function, after checking the number of arguments to the program first.
Pass the name of the file to the function that counts the lines in the file:
#include <stdio.h>
void numeroLinhas(const char *filename)
{
int ch;
int linhas = 0;
FILE *fp = fopen(filename, "r");
if (fp == 0)
{
fprintf(stderr, "Failed to open file %s for reading\n", filename);
return;
}
while ((ch = fgetc(fp)) != EOF)
{
if (ch == '\n')
linhas++;
}
fclose(fp);
fprintf(stats, "linhas: %d no %s\n", linhas, filename);
}
int main (int argc, char *argv[])
{
for (int i = 1; i < argc; i++)
numeroLinhas(argv[i]);
return 0;
}
Error check fopen() calls; they fail. Handle the int returned by fgetc() correctly. Change the function to return void since there isn't a return. Report errors on standard error. Include file name in the outputs (error and routine). Invoke the function on all arguments provided. Don't say anything if no arguments are provided — or add if (argc < 2) { fprintf(stderr, "Usage: %s file [...]\n", argv[0]); return 1; } before the loop in main().
Sorry about mixed English/Portugese messages.
int numeroLinhas(char *filename){
char ch;
FILE *fp;
int linhas=0;
fp = fopen(filename,"r");
while( ( ch = fgetc(fp) ) != EOF ){
if(ch=='\n'){
linhas++;
}
}
fclose(fp);
fprintf(stats, "linhas: %d\n", linhas);
}
int main (int argc, char *argv[]){
//should have an if for argc size
char *filename = argv[1];
numeroLinhas(filename);
}

Better way to convert string to integer

I wrote this code to read a variable in a .txt file, ignore the first character and convert into a integer.It works but looks dumb, is there a better way to do this? I'm using just one string here but it's supposed to work with four.
void read(char a[])
{
int i;
char a1[3];
for (i = 0; i<3; ++i){
a1[i]= a[i+1];
}
int b1 = atoi(a1);
}
int main()
{
FILE *file;
file = fopen( "file.txt", "r");
if (file == NULL) {
printf( "Arquivo nao encontrado\n");
}
char a[4];
fscanf(file, "%s\n",&a);
read(a);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
const char filename[] = "file.txt";
FILE *fp = fopen(filename, "r");
if (fp == 0)
{
fprintf(stderr, "Failed to open file %s for reading\n", filename);
return(EXIT_FAILURE);
}
int value;
if (fscanf(fp, "%*c%d", &value) != 1)
{
fprintf(stderr, "Failed to read integer value from file %s\n", filename);
fclose(fp);
return EXIT_FAILURE;
}
printf("Read %d\n", value);
fclose(fp);
return 0;
}
The %*c reads a single character but does not assign it. The * to suppress an assignment is a general mechanism in the scanf()
family of functions.
Untested code.

Pass stream by reference

I am suppose to pass stream, which is a pointer, by reference. So I am passing this as a pointer to a pointer. Can someone please verify my code?
int main(int argc, char** argv)
{
FILE *stream;
printf("LINES: %d\n",scan(stream));
}
int scan(FILE *(*stream))
{
stream = fopen("names.txt", "r");
int ch = 0, lines=0;
while (!feof(*stream))
{
ch = fgetc(*stream);
if (ch == '\n')
{
lines++;
}
}
fclose(*stream);
return lines;
}
No output received.
Your code has design issues. What exactly do you want to achieve?
If you just want to count the lines, make the FILE * local to your function:
int count_lines(const char *filename)
{
FILE *stream = fopen(filename, "r");
int lines = 0;
while (1) {
int c = fgetc(stream);
if (c == EOF) break;
if (c == '\n') lines++;
}
fclose(stream);
return lines;
}
If you want to do a regular file operation (read, write, seek, rewind etc.) to a file that has already been opened with fopen, just pass the handle as FILE *:
int fget_non_space(FILE *stream)
{
int c;
do {
c = fgetc(stream);
} while (isspace(c));
return c;
}
In that case, both fopen and fclose are called outside this function. (You don't call fclose in your program, which you should, even if the operating system makes sure to close the file automatically after exiting.)
Passing a pointer to the file handle, FILE **, makes sense only if you want to change that file handle itself in the function, for example by calling fopen:
int fopen_to_read(FILE **FILE pstream, const char *fn)
{
*pstream = fopen(fn, "r");
return (*pstream != NULL) ? 0 : -1;
}
Even then, it would be better to return the file handle, as fopen does.
Your example code leaves the open filehandle accessible in main, but you don't do anything with it, you don't even close it. Is that what you want? I doubt it.
Use
int scan(FILE **stream) //no need for brackets
{
*stream = fopen("names.txt", "r"); //* is for dereferencing
if(*stream==NULL) // Checking the return value of fopen
{
printf("An error occured when opening 'names.txt'");
return -1;
}
int ch = 0, lines=0;
while ((ch = fgetc(*stream))!=EOF) //while(!feof) is wrong
{
if (ch == '\n')
{
lines++;
}
}
fclose(*stream); // Close the FILE stream after use
return lines;
}
int main(void)
{
FILE *stream;
printf("LINES: %d\n",scan(&stream)); //Pass address of `stream`. The address is of type `FILE**`
}
Replace
stream = fopen("names.txt", "r");
with
*stream = fopen("names.txt", "r");
Also
printf("LINES: %d\n",scan(stream));
with
printf("LINES: %d\n",scan(&stream));

Is it necessary to pass the file pointer to main?

I am quite new to C programming and I have to make a program that asks the user to enter the file name to open and then open that file and print the values sorted.
Will I need to pass the file pointer to main, or can I just open the file in one function and I can work with the file throughout the other functions?
int getFile ()
{
char file_name[100];
FILE* fp;
int rc;
printf("Enter the file name: ");
rc = scanf("%s", file_name);
if (rc != 1)
printf ("error");
fp = fopen(file_name, "r");
return 0;
}
Do I have to pass the file pointer from here to main?
Just pass the file name as an argument to main function, it maybe what you want.
You might use int main(int argc, char* argv[]), an example:
int main(int argc, char* argv[])
{
FILE* fp;
if (argc == 1)
printf("usage : a.out filename\n");
else
{
if (fp = fopen(*++argv, "r") != NULL)
{
/*your code here*/
}
}
return 0;
}
You can open the file in one function and pass it as argument to other functions. For example, for the pre-open file handle stdin, you could use it as:
char mystring [100];
fgets (mystring , 100 , pFile)

Simple Program Segmentation Faults

I'm new to C, and I've been trying to figure out pointers.
This program works with -i but segfaults after a few lines and -f segfaults right away.
#include <stdio.h>
#include <string.h>
void search_and_print ( char pattern[], FILE* search_file );
int main ( int argc, char *argv[] ) {
const char TOO_MANY_VARIABLES[] = "Too many arguments from the command line!";
const char NOT_ENOUGH_VARIABLES[] = "\nUSAGE: a.out [-i] [-f filename] (Search Pattern)\n";
if (argc < 2) { printf(NOT_ENOUGH_VARIABLES); return(1);}
// If input
if (strcmp(argv[1],"-i") == 0) {
char *pattern = argv[2];
search_and_print(pattern, stdin);
}
// If file
if (strcmp(argv[1],"-f") == 0) {
char *pattern = argv[3];
// Check if file exists
// Open file
FILE *file = fopen( argv[2], "r" );
search_and_print(pattern, file);
fclose( file );
}
}
void search_and_print ( char pattern[], FILE* search_file ) {
// Read through file
const int MAX_CHARACTERS_PER_LINE = 1000;
char* line[MAX_CHARACTERS_PER_LINE];
while ( fgets(*line, MAX_CHARACTERS_PER_LINE, search_file) != NULL )
if ( strstr(*line, pattern) != NULL )
printf(*line);
}
You have quite a few bugs here.
char* line[MAX_CHARACTERS_PER_LINE];
defines an array of 1000 pointers, not characters. fgets(*line, ... passes the first of those pointers, which is uninitialized, to fgets, most likely causing a segvio.
printf(*line);
The first argument to printf is a format. Never ever pass user input as the format, as this opens a huge security hole in your program ... see http://en.wikipedia.org/wiki/Uncontrolled_format_string
You should use fputs(line) or printf("%s", line) (once you fix the declaration of line).
int main
You don't return a value (except in the error case) ... that results undefined behavior.
FILE *file = fopen( argv[2], "r" );
You should check whether this succeeds. If the file can't be opened (e.g., it doesn't exist), passing it to fgets results in undefined behavior.
if (argc < 2) { printf(NOT_ENOUGH_VARIABLES); return(1);}
This test isn't sufficient for your -f case.
you don't need
char* line[MAX_CHARACTERS_PER_LINE];
this is a pointer to arrays (maybe useful if you want to store the file or input line by line) and you haven't allocated it first. so the seg fault is quite obvious.
change your search_and_print to this:
void search_and_print ( char pattern[], FILE* search_file ) {
// Read through file
const int MAX_CHARACTERS_PER_LINE = 1000;
char line[MAX_CHARACTERS_PER_LINE];
while ( fgets(line, MAX_CHARACTERS_PER_LINE, search_file) != NULL )
if ( strstr(line, pattern) != NULL )
printf("%s\n", line);
}
additional to Jim Balter's very good advices, also I would suggest using getopt for parsing your parameters.
For future reference for anyone, here is the code with the suggested changes that seems to work. Thanks again for the help!
#include <stdio.h>
#include <string.h>
void search_and_print ( char pattern[], FILE* search_file );
int usage(const char* err);
const char USAGE[] =
"\nUSAGE: a.out [-i] [-f filename] (Search Pattern)";
int main ( int argc, char *argv[] ) {
const char TOO_MANY_VARIABLES[] = "Too many arguments from the command line!";
if (argc < 2) return usage("Not enough options");
if (argc > 4) return usage("Too many arguments from the command line!");
// If input
if (strcmp(argv[1],"-i") == 0) {
if (argc > 2) {
char *pattern = argv[2];
search_and_print(pattern, stdin);
}
else {
printf("Need a pattern to search by!");
return 1;
}
}
// If file
if (strcmp(argv[1],"-f") == 0) {
if (argc > 3) {
char *pattern = argv[3];
// Open file
FILE *file = fopen( argv[2], "r" );
// Check if file exists
if ( file != NULL) {
search_and_print(pattern, file);
fclose( file );
} else {
printf("File not found!");
return 1;
}
} else {
printf("Need a pattern to search by!");
return 1;
}
}
return 0;
}
int usage(const char* err) {
fprintf(stderr, "%s\n%s\n", err, USAGE);
return 1;
}
void search_and_print ( char pattern[], FILE* search_file ) {
const int MAX_CHARACTERS_PER_LINE = 1000;
char line[MAX_CHARACTERS_PER_LINE];
// Read through file
while ( fgets(line, MAX_CHARACTERS_PER_LINE, search_file) != NULL )
if ( strstr(line, pattern) != NULL )
printf("%s", line);
}

Resources