I know it's good practice closing every file pointer that is opened, but I've been thinking that fclose() only actually does something when someone is working with files on write/all mode where the user actually needs to save the new content on the file.
But does having no fclose() actually affects read only pointers in any way? I've done some testing but I haven't gotten anything different from when I was using fclose().
Is there something else that I should be aware?
Opened file pointers may leak without fclose() and they may prevent from opening more files when you do file operations many times.
This is a test code that do fopen() many times.
#include <stdio.h>
int main(void) {
int i;
for (i = 0; i < 1000; i++) {
FILE* fp = fopen("test.txt", "r");
int data;
printf("%3d : ", i);
if (fp != NULL) {
if (fscanf(fp, "%d", &data) == 1) {
printf("data = %d\n", data);
} else {
puts("read failed");
}
fclose(fp);
} else {
puts("fopen failed");
}
}
return 0;
}
This code successfully done 1000 operations on Wandbox.
On the other hand, When I commented out the fclose(fp); line, the operation began to fail after successful 251 operations.
When working with files I open file with "w+" to create or overwrite existing file. And then when I want to write new entry to the file I open it again with "wa" write what you need using fprintf and then close it.
Related
I have this function for initializing admin account,
the adminAcc.txt should be initialized only once, regardless of how many times the program has been executed. .
The code does not work tho, it still initializes the file everytime i run the program,
anyone can help? ty...
static int file_initialized = 0; //global variable
void initAdmin(){
struct form adminAcc;
FILE *fptr;
if((fptr = fopen("adminAcc.txt", "a")) == NULL){
printf("\nError Opening Admin File!");
getch();
system("cls");
main();
}
if(file_initialized != 1){
strcpy(adminAcc.username, "admin#gmail.com");
strcpy(adminAcc.password, "admin");
strcpy(adminAcc.roles, "admin");
fwrite(&adminAcc, sizeof(struct form), 1, fptr);
file_initialized = 1;
}else{
return;
}
fclose(fptr);
}
int main(){
initAdmin();
return 0;
}
If you want to retain the value of file_initialized across calls (as your code implies) you need to make it static:
static int file_initialized = 0;
If you only want to initialize it once across multiple program executions, then you need to persist the initialization state. Below I inspect the position of newly opened file and initialize it if empty:
#include <errno.h>
#include <stdio.h>
#define FILENAME "adminAcc.txt"
#define LEN 64
struct form {
char username[LEN];
char password[LEN];
char roles[LEN];
};
int initAdmin(){
FILE *fptr = fopen(FILENAME, "a");
if(!fptr) {
printf("Error Opening Admin File!\n");
return 1;
}
if(!ftell(fptr)) {
printf("initialize\n");
fwrite(
&(struct form) { "admin#gmail.com", "admin", "admin" },
sizeof(struct form),
1,
fptr
);
}
fclose(fptr);
return 0;
}
int main() {
initAdmin();
}
and example runs:
$ ./a.out
initialize
$ ./a.out
$
sorry for not updating...
this is how i solved the problem:
I included the library below, to use access() function.
#include <unistd.h>
The access() function checks to see if the file or directory specified by path exists and if it can be accessed with the file access permissions given by amode.
the function will only create and initialize a file named(filename) if the file does not exists, otherwise skip the initialization
void initAdmin(){
struct form adminAcc;
if(access("adminAcc.txt", F_OK) == -1){
FILE *fptr = fopen("adminAcc.txt", "w");
strcpy(adminAcc.username, "admin#gmail.com");
strcpy(adminAcc.password, "admin");
strcpy(adminAcc.roles, "admin");
fwrite(&adminAcc, sizeof(struct form), 1, fptr);
fclose(fptr);
}
}
The code does not work tho, it still initializes the file everytime i run the program
You cannot retain program state in the program across multiple runs. Every run of the program starts with a clean slate. Depending on the system on which it runs, however, you can generally retain state in the system. In this case, the natural state is whether the adminAcc.txt file exists, and possibly whether it has valid contents.
Although it is possible to check that before trying to open the file, a better paradigm is usually simply to attempt an operation (such as opening the file) and seeing whether that works. Because you need to do that anyway, and it's always possible for the system to be modified at just the right time such that the wanted operation fails even though an appropriate pre-check predicts that it will succeed.
So, something along these lines would make sense, then:
#include <stdio.h>
#include <string.h>
void initAdminIfNecessary(){
struct form adminAcc;
FILE *fptr;
if((fptr = fopen("adminAcc.txt", "ab")) == NULL){
fprintf(stderr, "\nError Opening Admin File!\n");
abort();
}
long fileSize = ftell(fptr);
if (fileSize == -1) {
fprintf(stderr, "\nError determining Admin File length!\n");
abort();
else if (fileSize == 0) {
strcpy(adminAcc.username, "admin#gmail.com");
strcpy(adminAcc.password, "admin");
strcpy(adminAcc.roles, "admin");
fwrite(&adminAcc, sizeof(struct form), 1, fptr);
// TODO: check return value
} else if (fileSize < sizeof(struct form)) {
fprintf(stderr, "\nAdmin File has invalid content\n");
abort();
}
fclose(fptr);
}
int main(){
initAdminIfNecessary();
return 0;
}
That opens the file as a binary (b) file in append (a) mode, which creates it if it does not already exist, but does not modify it if it does exist. The file is initially positioned at its end (and each write will go to the then-end of the file).
It uses ftell() to determine the length of the file, and acts accordingly.
I am writing a code that reads the characters from a file, and then if one of these characters is 'A' it should be changed to 'Z', after these changes are made it should write it back to the file (not append, but write), but whenever I open the file after running the code or it is empty, corrupted, or the compiler shouts at me (since I tried correcting it by making some changes, here is the code I have so far:
int main(){
char variable1[2000000];
FILE *filePointer1;
FILE *filePointer2;
int counter;
int exact_char_numb;
filePointer1 = fopen("File.txt", "r");
filePointer2 = fopen("File.txt", "w");
fread(variable1,2000000,sizeof(char), filePointer1);
for(counter = 0; counter<= 2000000 ; counter ++){
if(variable1[counter] == 'A'){
variable1[counter] = 'Z';
}
if(variable1[counter] == '+'){
exact_char_numb = counter; // I am using '+' to mark the end of
} // the file (for now)
}
fwrite(variable1,sizeof(char),exact_char_numb,filePointer2);
printf("%s\n", variable1);
printf("%d\n", exact_char_numb);
return 0;
}
In this call:
fwrite(variable1,sizeof(char),exact_char_numb,filePointer2);
the variable exact_char_numb is likely equal to zero, so you don't get any output. You should turn on all warnings and the compiler will complain to you about the variables that can be used without initializing them first.
"but whenever I open the file after running the code or it is empty, corrupted, or the compiler shouts at me..."
You should check whether the opening of the streams to file.txt were successful by checking the returned pointers for a null pointer before doing any operations with the streams:
if (!(filePointer1 = fopen("File.txt", "r")))
{
fputs("Error occurred at opening file to read!", stderr);
exit(1);
}
if (!(filePointer2 = fopen("File.txt", "w")))
{
fputs("Error occurred at opening file to write!\n", stderr);
exit(1);
}
Furthermore, you don´t need to do have two pointers to two different streams. Use r+ mode:
if (!(filePointer = fopen("File.txt", "r+")))
{
fputs("Error occurred at opening file!", stderr);
exit(1);
}
I am trying to write a simple C program which will read data from a csv file and perform some calculations on this data.
Unfortunately I have a problem where a file pointer of mine, fptr , is not being assigned a value after calling fopen(). I know this is the case after stepping through VS 2017's debugger. Yet I do not know why this is the case. This is a huge problem and means my program will throw some very nasty exceptions any time I try to read data from the file or close the file.
My code is below:
main.c
#include<stdio.h>
#include <stdlib.h> // For exit() function
#include"constants.h" //For access to all project constants
/***************************************************************************************************************
To keep the terminal from automatically closing
Only useful for debugging/testing purposes
***************************************************************************************************************/
void preventTerminalClosure() {
//flushes the standard input
//(clears the input buffer)
while ((getchar()) != '\n');
printf("\n\nPress the ENTER key to close the terminal...\n");
getchar();
}
/***************************************************************************************************************
Read the given input file
***************************************************************************************************************/
void readInputFile(char fileName[]) {
FILE *fptr;
char output[255];
//open the file
if (fptr = fopen(fileName, "r") != NULL) { //read file if file exists
//fscanf(fptr, "%[^\n]", output);
//printf("Data from the file:\n%s", output);
printf("<--Here-->");
}else {
printf("\nERROR 1: File %s not found\n", fileName);
preventTerminalClosure();
exit(1);
}
fclose(fptr); //close the file
}
/***************************************************************************************************************
* * * Main * * *
***************************************************************************************************************/
void main() {
char testName[MAX_NAME_SIZE];
printf("Hello World!\n");
printf("Please enter your name: ");
scanf("%s", testName);
printf("It's nice to meet you %s!", testName);
readInputFile("dummy.txt");
preventTerminalClosure(); //Debug only
}
I have made sure that my fake file does indeed exist and is located in the correct location. Otherwise my code would hit the else block inside of readInputFile(). That is something I have thoroughly tested.
There is clearly something basic that I am missing which explains this pointer behavior; but what that is, I am not sure. Any help would be appreciated! :)
Use parenthesis to enforce order, so that fptr is compared against NULL after it has been assigned value returned by fopen:
FILE *fptr;
char output[255];
//open the file
if ( (fptr = fopen(fileName, "r")) != NULL)
I have to write a program witch reads from a file received by line and then it overwrites it with the read words uppercased.
This is my code
void toUpperCase(char* string) {
int i=0;
while(string[i])
{
string[i]=toupper(string[i]);
i++;
} }
int main(int argc, char** argv) {
if(argc==1)
{
puts("Error: INSERT PATH");
exit(0);
}
char* file=argv[1];
FILE* fd=fopen(file,"r+");
if(fd<0)
{
perror("Error opening file: ");
exit(0);
}
char buffer[30][30];
int i=0;
while(!feof(fd))
{
fscanf(fd,"%s",buffer[i]);
i++;
}
int j=0;
for(j=0; j<i; j++)
{
toUpperCase(buffer[j]);
fwrite(buffer[j],strlen(buffer[j]),1,fd);
}
fclose(fd);
return 0; }
but this program appends the words contained in buffer[][] instead of overwriting the file.
If the file contain was something like pippo pluto foo then, after the execution is pippo pluto fooPIPPOPLUTOFOO instead of PIPPO PLUTO FOO.
Where am i wrong? Thank you
You have to reset the file position indicator using fseek, as fscanf will advance it. Something like
fseek(fd, length_of_read_string, SEEK_CUR);
This allows you to read the file in chunks, but it will be tricky to get right. Or of course reset it to the file start because you read everything in 1 go:
fseek(fd, 0L, SEEK_SET);
I strongly recommend writing the modified data into a new file, and then after the program has run, delete the initial file and rename the new one. That will also take care of another issue with your program, you are reading the entire file into memory before handling it.
If you want to do in-place translation that doesn't change lengths, you can open the source file in two streams and then do read-chunk, write-chunk in lockstep. That has the advantage of being super-easy to convert to a non-in-place version that will work with nonseekable files too (stdin/stdout, pipes, and sockets).
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h> //toupper
inline void upcaseStr(char* str){
for(;*str;str++) { *str=toupper(*str); }
}
int upcaseStream(FILE* in, FILE* out){
char buf[BUFSIZ]; //BUFSIZ is an implementation-defined constant for an optimal buffer size
while(fgets(buf, BUFSIZ, in)){
upcaseStr(buf);
if(fputs(buf, out) == EOF){ return 1; }
}
if(!feof){ return 1; }
return 0;
}
int main(int argc, char **argv)
{
//default in and out
FILE* in = stdin;
FILE* out = stdout;
if(argc == 2) {
in = fopen(argv[1], "r"); //for reading
out = fopen(argv[1], "r+"); //for writing (and reading) starting at the beginning
if(!(in && out)){
fprintf(stderr, "Error opening file %s for reading and writing: %s\n", argv[1], strerror(errno));
}
}
return upcaseStream(in, out);
}
If you do use the in-place version, then in the unlikely event that the if(fputs(buf, out) == EOF){ return 1; } line should return, you're screwed unless you have a backup copy of the file. :)
Note:
You shouldn't name your FILE pointers fd because C people will tend to think you mean "file descriptor". FILE is a struct around a file descriptor. A file descriptor is just an int that you can use for FILE access with the raw system calls. FILE streams are an abstraction layer on top of file descriptors--they aren't file descriptors.
As you read from the file, its internal position indicator gets moved. Once you start writing, you start writing from that position on, which happens to be at the end of the file. So you effectively append the data to the file.
Rewind the handle to reset the position indicator before writing into the file:
rewind(fp);
On a side note, you are reading the file incorrectly:
while(!feof(fd))
{
fscanf(fd,"%s",buffer[i]);
i++;
}
When you reach the end of the file, fscanf will return an error and not read anything, yet you still increment variable i, as if the read was successful. And then you check feof() for end-of-file, but i was already incremented.
Check feof() and return of fscanf() immediately after calling fscanf():
while(1)
{
int read = fscanf(fd,"%s",buffer[i]);
if( read != 1 )
//handle invalid read
if( feof(fd) )
break;
i++;
}
Think about what happens if the string is longer than 29 characters and/or the file contains more than 30 strings. char buffer[30][30];
Welcome to StackOverflow!
Reopening the stream with fopen with the "w" parameter:
fd=fopen(file, "w");
It opens the file and if there are any contents in the file, it clears them.
In my code below, the file is being written correctly as far as I can tell. When I look in the file floats.dat I see this stream of binary ÍÌL#33c#ÍÌÜ#ffFAßOeA^#^#bBf6zE33äCff<83>BÍ̦B
However my program always ends up triggering this if statement:
if(fread(inputFloats, sizeof(float), LENGTH, binaryFile) < LENGTH)
{
fprintf(stderr, "Problem reading some or all data from %s\n\n", binaryFileName);
return EXIT_FAILURE;
}
Does anybody see something I've done wrong here? Full code below.
#include <stdlib.h>
#include <stdio.h>
#define LENGTH 10
int main(void)
{
FILE *binaryFile, *textFile;
char *binaryFileName = "floats.dat", *textFileName = "floats.txt";
float floats[LENGTH] = {3.2, 3.55, 6.9, 12.4, 14.332, 56.5, 4003.4, 456.4, 65.7, 83.4};
float inputFloats[LENGTH];
int i;
if((binaryFile = fopen(binaryFileName, "r+")) == NULL)
{
fprintf(stderr, "Problem opening %s", binaryFileName);
}
if(fwrite(floats, sizeof(float), LENGTH, binaryFile) < LENGTH)
{
fprintf(stderr, "Problem writing some or all data to %s\n", binaryFileName);
return EXIT_FAILURE;
}
printf("DATA WRITTEN SUCCESSFULLY\n");
if(fread(inputFloats, sizeof(float), LENGTH, binaryFile) < LENGTH)
{
fprintf(stderr, "Problem reading some or all data from %s\n\n", binaryFileName);
return EXIT_FAILURE;
}
for(i = 0; i < LENGTH; i++)
{
printf("float[%d] = %f\n", i, floats[i]);
}
return EXIT_SUCCESS;
}
You're not working with text data so you should specify a binary mode when opening the file. Use r+b instead of r+
You need to fseek(binaryFile, 0, SEEK_SET) to "rewind" the file after writing. rewind can also be used for this case - fseek allows you to position the read/write pointer wherever you want.
The FILE structure keeps a record of where in the file it is currently pointing. Since you've just written to binaryFile, the file pointer is at the end of what you've written.
You therefore need to rewind the file, using fseek(binaryFile, 0, SEEK_SET); before you read.
You forgot to rewind your file before reading it:
rewind(binaryFile);
When you finish writing to the file, the FILE pointer is at the end of it, so of course if you try reading it will not work. Try using fseek to move the pointer to the beginning of the file before reading.
Please avoid this :
if((binaryFile = fopen(binaryFileName, "r+")) == NULL) {
and prefer this:
binaryFile = fopen(binaryFileName, "rb+");
if(!binaryFile) {