I'm trying to read a file with some tokens, using fgets and strtok_s, I wanted to use strtok, but it's not safe, and I couldn't find any good example in how to use it. After some checks I've made, what's happening it's reading from the file only the words such as the first word before of the token (i.e. if the string is "1,2,3,4,5,1" and the token is ",", so it would read 1 twice in the same loop, and would exit the while loop.
The code:
int val_to_add = 0;
TreeNode * head = new TreeNode(NIL,NIL,NIL,NIL);
FILE * fp;
char line[MAX_LINE];
char *pstr;
errno_t err;
char * context = NULL;
if (argc == 1)
{
//There was no file option.
}
else
{
if ((err = fopen_s(&fp, argv[1], "r")) != 0)
{
fprintf(stderr, "ERROR!\nCannot open file '%s'.\n",argv[1]);
exit(1);
}
while (fgets(line, MAX_LINE, fp) != NULL)
{
pstr = strtok_s(line, ",", &context);
val_to_add = atoi(pstr);
add_treenode(head, val_to_add);
printf("%s\n",pstr);
}
}
Why is the loop is working only once?
Related
I want to get the last record in a binary file, extract its ID and add one to it to mimic an auto-incrementing ID feature. So for example, if the previous user had an ID of 1, then the next user should have an ID of 2, and so on. The issue is I can't get the last record of the binary file using the code below. How can I go about doing this? Here's my code so far:
#include <stdio.h>
struct Applicant
{
int id;
};
void increment()
{
char fileName = "data.dat";
FILE *file;
// Instantiate the applicant struct
struct Applicant applicant;
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Open the file in 'append binary' mode
file = fopen(fileName, "ab");
// If file does not exist, print error message and exit
if (!file)
{
printf("\nSorry, that file does not exist");
exit(1);
}
// Read the last applicant to get their id
while(1)
{
fread(&previousApplicant, sizeof(previousApplicant), 1, file);
if (feof(file)) { break; }
}
// If there's no previous applicant, set the ID to 100
if (previousApplicant.id == 0)
{
applicant.id = 100;
}
// Otherwise just increment the previous applicant's id and assign it to the new applicant
else
{
applicant.id = previousApplicant.id + 1;
}
fwrite(&applicant, sizeof(applicant), 1, file);
fclose(file);
}
To achieve this, I'm using structs
The way I'd suggest doing it is to call fseek() to seek directly to the last record in the file, read it in to RAM, update it, and write it out again. That way you don't have to read in every record in the file just to update the last one, which would be really inefficient if the file is large and/or you have to perform this operation often.
Something like this:
#include <stdio.h>
#include <stdlib.h>
struct Applicant
{
int id;
};
void increment()
{
const char fileName[] = "data.dat";
// Open the file in 'append binary' mode
FILE *file = fopen(fileName, "a+b");
if (file == NULL)
{
printf("Couldn't open file [%s]\n", fileName);
return;
}
// Seek to the end of the file
if (fseek(file, 0, SEEK_END) != 0)
{
printf("Couldn't seek to the end of the file!\n");
fclose(file);
return;
}
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Check to make sure the file is big enough to hold at least one record
const long fileLengthBytes = ftell(file);
if (fileLengthBytes < sizeof(previousApplicant))
{
printf("File is to short to contain a full record!?\n");
fclose(file);
return;
}
// Check to make sure the file length is an even multiple of the record-length
if ((fileLengthBytes % sizeof(previousApplicant)) != 0)
{
printf("File length (%li) is not a multiple of record size (%zu)!?\n", fileLengthBytes, sizeof(previousApplicant));
fclose(file);
return;
}
// Seek backwards to the start of the last record in the file
if (fseek(file, -sizeof(previousApplicant), SEEK_CUR) != 0)
{
printf("Couldn't seek to the start of the last record in the file!\n");
fclose(file);
return;
}
// Read in the last record in the file
if (fread(&previousApplicant, sizeof(previousApplicant), 1, file) != 1)
{
printf("Couldn't read the last record in the file!\n");
fclose(file);
return;
}
// Increment the record
printf("Incrementing ID of last record in the file from %i to %i\n", previousApplicant.id, previousApplicant.id+1);
previousApplicant.id++;
// Finally, write out the new record in the file with incremented ID
if (fwrite(&previousApplicant, sizeof(previousApplicant), 1, file) != 1)
{
printf("Couldn't write the last record in the file!\n");
fclose(file);
return;
}
// success!
fclose(file);
}
int main(int, char **)
{
increment();
return 0;
}
There were a few problems with your code. You were reading the applicant ID into the applicant struct variable rather than the previousApplicant variable and when reading and writing files it is best to separate out the tasks: first open the file for reading, then when you're ready to write, close it and open it for writing.
Here's the modified working code which will do what you want:
#include <stdio.h>
#include <stdlib.h>
struct Applicant
{
int id;
};
void increment()
{
char* fileName = "data.dat";
FILE *file;
// Instantiate the applicant struct
struct Applicant applicant;
// Will contain details of the last applicant in the file
struct Applicant previousApplicant;
// Open the file in 'append binary' mode
file = fopen(fileName, "rb");
// If file does not exist, print error message and exit
if (!file)
{
printf("\nSorry, that file does not exist");
exit(1);
}
// Read the last applicant to get their id
while(1)
{
fread(&previousApplicant, sizeof(applicant), 1, file);
if( feof(file) ) break;
}
// If there's no previous applicant, set the ID to 100
if (previousApplicant.id == 0)
{
applicant.id = 100;
}
// Otherwise just increment the previous applicant's id and assign it to the new applicant
else
{
applicant.id = previousApplicant.id + 1;
}
fclose(file);
FILE* out = fopen(fileName, "ab");
fwrite(&applicant, sizeof(applicant), 1, out);
printf("%d\n", applicant.id);
fclose(out);
}
void main() {
increment();
}
I am not sure I got the logic of what you are trying to do. As pointed above, you can use just fseek() to move the current pointer for the file.
from the [documentation][1]
int fseek(
FILE *stream,
long offset,
int origin
);
int _fseeki64(
FILE *stream,
__int64 offset,
int origin
);
As for the offset: you can use this pre-defined constants: SEEK_CUR meaning the current position in file, SEEK_SET meaning the beginning and SEEK_END pointing, well, to the END of the file. The current offset from the beginning of file you can get by calling fseek()'s sister, ftell(), that returns a long with the position. First byte is 0. You can for sure seek for any position.
An example:
At first consider this code:
int create(const char* f_name, unsigned int n_rec)
{
Applicant one = {100};
FILE* f = fopen(f_name, "wb");
if (f == NULL) return -1;
for (unsigned i = 0; i < n_rec; i += 1, one.id+= 1)
if (fwrite(&one, sizeof(one), 1, f) != 1)
return 1 + i;
fclose(f);
return n_rec;
}; // create()
This function creates the file and writes down the supplied number of IDs. e.g.
create( "x.dat", 300 )
will go as expected and create x.dat and write 300 IDs starting at 100 --- inclusive --- in the file.
Then the following function in the example below reopens the file, get the last ID used and returns it, and you can use it as a prototype:
int get_last_id(const char* f_name)
{
Applicant one = {0};
int res = 0;
FILE* f = fopen(f_name, "rb+");
if (f == NULL) return -1;
res = fseek(f, SEEK_END, SEEK_SET);
if (res != 0) return -2;
res = fseek(f, -(long) (sizeof(one)), SEEK_END);
if (res != 0)
{
printf("res = %d\n", res);
perror("Error in seek()");
return -3;
}
printf("(file position: byte #%ld)\n", ftell(f));
if (fread(&one, sizeof(one), 1, f) != 1) return -4;
fclose(f);
return one.id;
}
The lines you want are these:
Applicant one = {0};
fseek(f, SEEK_END, SEEK_SET);
fseek(f, -(long) (sizeof(one)), SEEK_END);
fread(&one, sizeof(one), 1, f);
You go to the end if file, then goes back 1 record and read it.
sample output
5 Ids written to "sample.dat"
now open file and get the last ID used
(file position: byte #16)
Last ID on "sample.dat" is 104
For this
C code
#include <stdio.h>
typedef struct
{ int id;
} Applicant;
int create(const char*,unsigned);
int get_last_id(const char*);
int main(void)
{
const char* f_name = "sample.dat";
const unsigned initial_size = 5;
int res = 0;
// a few ids are written to file
if ( (res = create(f_name, initial_size)) < 0) return -1;
printf("\
%d Ids written to \"%s\"\n\
now open file and get the last ID used\n",
res, f_name);
// get the last id on file
res = get_last_id(f_name);
if (res < 0)
{
printf("Could not get id from file (%d)\n", res);
return -1;
}
else
printf("Last ID on \"%s\" is % d\n", f_name, res);
return 0;
}
int get_last_id(const char* f_name)
{
Applicant one = {0};
int res = 0;
FILE* f = fopen(f_name, "rb+");
if (f == NULL) return -1;
res = fseek(f, SEEK_END, SEEK_SET);
if (res != 0) return -2;
res = fseek(f, -(long) (sizeof(one)), SEEK_END);
if (res != 0)
{
printf("res = %d\n", res);
perror("Error in seek()");
return -3;
}
printf("(file position: byte #%ld)\n", ftell(f));
if (fread(&one, sizeof(one), 1, f) != 1) return -4;
fclose(f);
return one.id;
}
int create(const char* f_name, unsigned int n_rec)
{
Applicant one = {100};
FILE* f = fopen(f_name, "wb");
if (f == NULL) return -1;
for (unsigned i = 0; i < n_rec; i += 1, one.id+= 1)
if (fwrite(&one, sizeof(one), 1, f) != 1)
return 1 + i;
fclose(f);
return n_rec;
}; // create()
```
[1]: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fseek-fseeki64?view=msvc-160
I am writing a XOR encryption program which works fine during encryption but during decryption
the
char ca2=fgetc(f);
gets stuck at one point and no decryption takes place after that my best guess about the problem is (the encrypted file contains all sorts of characters ) as soon as fgetc reaches EOF mark which can be present before the actual end of the file it gets stuck there and stop reading the next characters .
is this some kind of limitation of getc() ? here is my rubbish code
int get_file_size(char filename[])
{
FILE *p_file = NULL;
p_file = fopen(filename,"rb");
fseek(p_file,0,SEEK_END);
int size = ftell(p_file);
fclose(p_file);
return size;
}
int endec(char filename[],char psdw[])
{
FILE *f;
int hashed=0,ed=0;
int inphash=inhash(psdw);
inphash=inphash%50;
f=fopen(filename,"r");
if(f==NULL)
printf("failed");
char temps[999999];
long int crs=0,j=0;
int filesz=get_file_size(filename);
printf("file size = %d\n\n",filesz);
while(1){
inphash=inphash+2;
char ca=(char)inphash;
char ca2=fgetc(f);
printf("%c\n",ca2);
if(crs>=filesz)
break;
temps[crs]= ca2 ^ ca;
crs++;
}
fclose(f);
printf("%d",strlen(temps));
FILE *fp;
fp=fopen(filename,"wt");
for(j=0;j<crs;j++){
putc (temps[j] , fp);
printf("%c",temps[j]);
}
fclose(fp);
}
Your problem is right here:
f=fopen(filename,"r");
You open the file for text reading, not for binary. Your file size function gets it right, but your decoder function does not.
The idiomatic way to read a file character by character using the C-style IO routines is like this:
f = fopen(filename, "rb");
if (!f)
// handle error
int c; // NOTE: int, not char!
while ( (c = fgetc(f)) != EOF )
{
// do something with 'c'
}
This idiom does not require you to get the file size as a separate operation. You can rewrite your XOR "encryption" routine with a simple loop of the above form. It will be much clearer and more concise.
Your entire decoder function could be rewritten as follows: (minus the debug code)
int endec(char filename[], char psdw[])
{
int inphash = inhash(psdw) % 50;
char temp[999999]; // really, should be std::vector<char>
FILE *f;
if ( (f = fopen(filename, "rb")) == NULL )
{
printf("opening for read failed\n");
return -1;
}
size_t crs = 0;
int c;
while ( (c = fgetc(f)) != EOF )
{
inphash += 2;
temp[crs++] = (char)(inphash ^ c);
}
fclose(f);
if ( (f = fopen(filename, "wt")) == NULL )
{
printf("opening for write failed\n");
return -1;
}
if (fwrite(temp, crs, 1, f) != crs)
{
printf("short write\n");
fclose(f);
return -1;
}
fclose(f);
return 0;
}
Not stellar error handling, but it is error handling.
I have a C program that finds duplicate files within a directory. The program is executed on the command line and passed 2 arguments. One is the parent directory, and argument two is the file name. It is working code in c, but I have a GUI and other files for "microservices" written in c++.
How would one call this C code from a c++ file?
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
//Compile: gcc dreamduplicatefinder.c -o dreamduplicatefinder.exe
//Run: ./dreamduplicateFinder.exe parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//printf(dir); //error could becuase trying to open shortcut or corrupt folder.
printf("%s\n",path);
perror("Failed to open directory");
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("%s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
//Step 2...
//size matches, files can be same; confirm it by matching both file contents...
int fdOriginal = open(originalFile, O_RDONLY);
int fdCurr = open(currFile, O_RDONLY);
if (fdOriginal == -1 || fdCurr == -1)
return false; //error occurred, not sure if file is duplicate...
//we will read file in small chunks and compare...
int chunkSize = 1024, bytesRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
while (true)
{
//read file in chunk...
bytesRead = read(fdOriginal, bufferOriginal, chunkSize);
if (bytesRead <= 0)
break; //end of file...
bytesRead = read(fdCurr, bufferCurr, bytesRead);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
My errors include: (from compareFiles function)
2x 'open' identifier not found
2x 'read' identifier not found
The working code for those curious.
Thank you #MarcusMüller & #JesperJuhl
#include "stdafx.h" //there is nothing in this header
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Compile: gcc <name of this file>.cpp -o <nameOfThisFile>.exe
//Run: <nameOfThisFile> parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//possibly trying to open shortcut or corrupt folder typically.
printf("Failed to open directory %s \n",path);
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("This is a duplicate! %s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
FILE* fdOriginal;
if (fdOriginal = fopen(originalFile, "r")) {
if (fdOriginal == NULL) { fputs("File error", stderr); return false; }
}
else return false; //error occurred, not sure if duplicate
FILE* fdCurr;
if (fdCurr = fopen(currFile, "r")) {
if (fdCurr == NULL) { fputs("File error", stderr); return false; }
}
else return false;
int chunkSize = 1024, objsRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
if (bufferOriginal == NULL) { fputs("Memory error for buff orig", stderr); exit(2); }
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
if (bufferCurr == NULL) { fputs("Memory error for buff curr", stderr); exit(2); }
while (true)
{
//read file in chunk...
//std::size_t fread( void* buffer, std::size_t size, std::size_t count, std::FILE* stream );
objsRead = fread(bufferOriginal, sizeof(char), chunkSize , fdOriginal);
if (objsRead <= 0)
break; //end of file...
objsRead = fread(bufferCurr, sizeof(char), objsRead, fdCurr);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
You usually just wouldn't do that. You'd wrap it in a C function, and compile it to an object file.
Then you'd include your C header with extern "C" {…}, and just call that function from C++.
When building your executable, you'd link in the object file containing your C function. Done!
Note: C isn't C++, and albeit your code not being illegal in C++ (as far as I can instantly tell), it does very "ugly" things (like #defineing true and false – ugh, that would already be a bad idea in C, to be honest). So, deal with it like you would deal with code in Fortran, or Java, or any other language that has a calling convention that you can use from C++ (which, usually, is the C calling convention): Just use it as an extern object.
Using ::open and ::read should cause the functions to be found.
You may also want to replace the C headers (like "string.h") with their C++ equivalent versions (like "cstring").
Your defines for true and false should also go. In C++ those are proper bools, not integers. This means the return type of CompareFiles should be changed to bool.
And you should wrap duplicateCount in an anonymous namespace - or return it from the function that updates it (either by returning a small struct with two ints, or by using a std::pair or std::tuple) - global variables are evil.
I am writing a XOR encryption program which works fine during encryption but during decryption
the
char ca2=fgetc(f);
gets stuck at one point and no decryption takes place after that my best guess about the problem is (the encrypted file contains all sorts of characters ) as soon as fgetc reaches EOF mark which can be present before the actual end of the file it gets stuck there and stop reading the next characters .
is this some kind of limitation of getc() ? here is my rubbish code
int get_file_size(char filename[])
{
FILE *p_file = NULL;
p_file = fopen(filename,"rb");
fseek(p_file,0,SEEK_END);
int size = ftell(p_file);
fclose(p_file);
return size;
}
int endec(char filename[],char psdw[])
{
FILE *f;
int hashed=0,ed=0;
int inphash=inhash(psdw);
inphash=inphash%50;
f=fopen(filename,"r");
if(f==NULL)
printf("failed");
char temps[999999];
long int crs=0,j=0;
int filesz=get_file_size(filename);
printf("file size = %d\n\n",filesz);
while(1){
inphash=inphash+2;
char ca=(char)inphash;
char ca2=fgetc(f);
printf("%c\n",ca2);
if(crs>=filesz)
break;
temps[crs]= ca2 ^ ca;
crs++;
}
fclose(f);
printf("%d",strlen(temps));
FILE *fp;
fp=fopen(filename,"wt");
for(j=0;j<crs;j++){
putc (temps[j] , fp);
printf("%c",temps[j]);
}
fclose(fp);
}
Your problem is right here:
f=fopen(filename,"r");
You open the file for text reading, not for binary. Your file size function gets it right, but your decoder function does not.
The idiomatic way to read a file character by character using the C-style IO routines is like this:
f = fopen(filename, "rb");
if (!f)
// handle error
int c; // NOTE: int, not char!
while ( (c = fgetc(f)) != EOF )
{
// do something with 'c'
}
This idiom does not require you to get the file size as a separate operation. You can rewrite your XOR "encryption" routine with a simple loop of the above form. It will be much clearer and more concise.
Your entire decoder function could be rewritten as follows: (minus the debug code)
int endec(char filename[], char psdw[])
{
int inphash = inhash(psdw) % 50;
char temp[999999]; // really, should be std::vector<char>
FILE *f;
if ( (f = fopen(filename, "rb")) == NULL )
{
printf("opening for read failed\n");
return -1;
}
size_t crs = 0;
int c;
while ( (c = fgetc(f)) != EOF )
{
inphash += 2;
temp[crs++] = (char)(inphash ^ c);
}
fclose(f);
if ( (f = fopen(filename, "wt")) == NULL )
{
printf("opening for write failed\n");
return -1;
}
if (fwrite(temp, crs, 1, f) != crs)
{
printf("short write\n");
fclose(f);
return -1;
}
fclose(f);
return 0;
}
Not stellar error handling, but it is error handling.
I get a "Segmentation fault" for the following C++ code:
#include <cstdio>
int main(int, char**) {
FILE *fp;
fp = fopen("~/work/dog.txt", "w");
fprintf(fp, "timer, timer3, timer5, timer6, timer7");
fclose(fp);
}
Your path is invalid and will never work, thus fopen sets fp to NULL and you get a segfault. Hint: the ~ character is expanded by the shell, you can't use it in an argument to fopen.
A correct, safe implementation of what you're trying to do might look as follows. This is tested. It's also the reason why sane people don't write in C unless they have no other way of doing it :)
// main.cpp
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
int main(int, char**)
{
const char * home = getenv("HOME");
if (home == NULL || strlen(home) == 0 || access(home, F_OK) == -1) abort();
const char * subPath = "/work/dog.txt";
char * path = (char*)malloc(strlen(home) + strlen(subPath) + 1);
if (path == NULL) abort();
strcpy(path, home);
strcat(path, subPath);
FILE *fp = fopen(path, "w");
if (fp != NULL) {
fprintf(fp, "timer, timer3, timer5, timer6, timer7");
fclose(fp);
}
free(path);
}
A few things:
you need to check fp for NULL before using it, else you'll get a segfault whenever the file isn't found.
you need to resolve the full path before passing it to fopen (fopen doesn't know what to do with "~")
example:
FILE *fp = NULL;
char path[MAX];
char *home = getenv ("HOME");
if ( home )
{
snprintf(path, sizeof(path), "%s/work/dog.txt", home);
// now use path in fopen
fp = fopen(path, "w");
if ( fp )
{
fprintf(fp, "timer, timer3, timer5, timer6, timer7");
fclose(fp);
}
else
{
std::cout << "your dog is missing" << std::endl;
}
else
{
std::cout << "You are homeless" << std::endl;
}
Segfault happens is the file you're trying to open does not exist. This has nothing to do with Qt.
Test for the nullity of 'fp' and handle the error correctly. Something like
FILE *fp = fopen("/path/to/work/dog.txt", "w");
if (fp == NULL)
{
printf("File does not exist.\n");
// throw exception or whatever.
}