Related
I am trying to copy binary files from src to dst. This script seems to copy all of the bytes. BUT when I open both files in Hex Workshop I see that dst file is always missing 3 bytes at the end of the file. These 3 bytes should have been 00 00 00, this problem prevents me from opening dst file.
void binaryCopy(char **argv) {
int *buf = 0;
int elements = 0;
int size = 0, wantOverwrite = 0;
FILE *src = fopen(argv[SRC_POS], "rb");
FILE *dst = fopen(argv[DST_POS], "w+b");
if (src) {
if (dst) {
wantOverwrite = overwrite();
}
if (wantOverwrite) {
fseek(src, 0L, SEEK_END);
size = ftell(src);
fseek(src, 0L, SEEK_SET);
buf = (int *)malloc(size);
elements = fread(buf, BYTE_SIZE, size / BYTE_SIZE, src);
fwrite(buf, BYTE_SIZE, elements, dst);
printf("copy completed");
free(buf);
}
}
fclose(dst);
fclose(src);
}
There are several problems in your function as written.
fopen(dstFilename, "w+b"); truncates the file, so your overwrite check later is meaningless.
You're not checking for NULL after malloc, and your buffer should be an unsigned char* since that is what fread/fwrite will interpret it as.
At the end, both fclose functions could be called with NULL file pointers likely resulting in a crash. You should move them into the scopes where you know each was successfully opened.
The big problem, the one that prompted this question, is that you are not handling cases where the size of the file is not an even multiple of whatever BYTE_SIZE is. Since you allocated enough memory for the whole file you should just read and write the whole file. fread(buf, 1, size, src); and fwrite(buf, 1, size, dst);. In general it is best to make the element size parameter of fread/fwrite 1 and the count the number of bytes you want to read or write. There's no math to go wrong, and you can tell exactly how many bytes were read/written.
Here's a version of your original function that I've corrected and annotated so it works if nothing goes wrong.
void originalBinaryCopy(const char *srcFilename, const char *dstFilename)
{
//odd size to ensure remainder
const size_t BYTE_SIZE = 777;
int *buf = 0;
int elements = 0;
int size = 0, wantOverwrite = 0;
FILE *src = fopen(srcFilename, "rb");
//This truncates dst, so the overwirte check is meaningless
FILE *dst = fopen(dstFilename, "w+b");
if (src)
{
if (dst)
{
fseek(src, 0L, SEEK_END);
size = ftell(src);
fseek(src, 0L, SEEK_SET);
//always check for NULL after malloc - This should be a char*
buf = (int *)malloc(size);
if (!buf)
{
fclose(dst);
fclose(src);
return;
}
elements = fread(buf, BYTE_SIZE, size / BYTE_SIZE, src);
fwrite(buf, BYTE_SIZE, elements, dst);
//added, copy remainder
elements = fread(buf, 1, size % BYTE_SIZE, src);
fwrite(buf, 1, size % BYTE_SIZE, dst);
//end
printf("copy completed %s -> %s\n", srcFilename, dstFilename);
free(buf);
}
}
//dst could be NULL here, move inside if(dst) scope above
fclose(dst);
//src could be NULL here, move inside if(src) scope above
fclose(src);
if (comp(srcFilename, dstFilename) != 0)
{
printf("compare failed - %s -> %s\n", srcFilename, dstFilename);
}
}
Notice how the remainder is handled at the end.
Here is how I would handle copying files along with a test suite to create, copy, and verify a set of files. It shows how to avoid truncating the destination if you don't want to and has quite a bit of error checking in the actual functions. I did not include any specific error checking on the caller side, but for real code I would have enumerated all of the possible errors and used those return values to pass to an error handling function that could print them out and possibly exit the program.
Manipulating files is one thing you want to be VERY careful about since there's potential for data loss if your code doesn't work, so before you use it with real files make sure it's 100% solid with test files.
#include <malloc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define TEST_FILE_MIN 1024
#define TEST_FILE_MAX 1024 * 1024
const char *src_pattern = "src_file_%08x.bin";
const char *dst_pattern = "dst_file_%08x.bin";
void createTestFiles(const char *pattern)
{
char filename[256] = { 0 };
char buffer[1024];
for (size_t i = 0; i < sizeof(buffer); ++i)
{
buffer[i] = rand();
}
for (size_t i = TEST_FILE_MIN; i <= TEST_FILE_MAX; i *= 2)
{
sprintf(filename, pattern, i);
FILE *dst = fopen(filename, "wb");
if (dst)
{
size_t reps = i / TEST_FILE_MIN;
for (size_t w = 0; w < reps; ++w)
{
fwrite(buffer, 1, sizeof(buffer), dst);
}
fclose(dst);
}
}
}
int comp(const char *srcFilename, const char *dstFilename)
{
FILE *src = fopen(srcFilename, "rb");
if (!src)
{
return -1;
}
//open for reading to check for existence
FILE *dst = fopen(dstFilename, "rb");
if (!dst)
{
fclose(src);
return -2;
}
fseek(src, 0, SEEK_END);
size_t srcSize = ftell(src);
fseek(src, 0, SEEK_SET);
fseek(dst, 0, SEEK_END);
size_t dstSize = ftell(dst);
fseek(dst, 0, SEEK_SET);
if (srcSize == 0 || dstSize == 0 || srcSize != dstSize)
{
fclose(src);
fclose(dst);
return -3;
}
unsigned char *srcBuf = (unsigned char *)calloc(1, srcSize);
unsigned char *dstBuf = (unsigned char *)calloc(1, srcSize);
if (!srcBuf || !dstBuf)
{
fclose(src);
fclose(dst);
return -4;
}
if (fread(srcBuf, 1, srcSize, src) != srcSize)
{
fclose(src);
fclose(dst);
return -5;
}
if (fread(dstBuf, 1, dstSize, dst) != dstSize)
{
fclose(src);
fclose(dst);
return -6;
}
fclose(src);
fclose(dst);
//result * 100 to make this error outside te range of the other general errors from this function.
int result = memcmp(srcBuf, dstBuf, srcSize) * 100;
free(srcBuf);
free(dstBuf);
return result;
}
void originalBinaryCopy(const char *srcFilename, const char *dstFilename)
{
//odd size to ensure remainder
const size_t BYTE_SIZE = 777;
int *buf = 0;
int elements = 0;
int size = 0, wantOverwrite = 0;
FILE *src = fopen(srcFilename, "rb");
//This truncates dst, so the overwirte check is meaningless
FILE *dst = fopen(dstFilename, "w+b");
if (src)
{
if (dst)
{
fseek(src, 0L, SEEK_END);
size = ftell(src);
fseek(src, 0L, SEEK_SET);
//always check for NULL after malloc - This should be a char*
buf = (int *)malloc(size);
if (!buf)
{
fclose(dst);
fclose(src);
return;
}
elements = fread(buf, BYTE_SIZE, size / BYTE_SIZE, src);
fwrite(buf, BYTE_SIZE, elements, dst);
//added, copy remainder
elements = fread(buf, 1, size % BYTE_SIZE, src);
fwrite(buf, 1, size % BYTE_SIZE, dst);
//end
printf("copy completed %s -> %s\n", srcFilename, dstFilename);
free(buf);
}
}
//dst could be NULL here, move inside if(dst) scope above
fclose(dst);
//src could be NULL here, move inside if(src) scope above
fclose(src);
if (comp(srcFilename, dstFilename) != 0)
{
printf("compare failed - %s -> %s\n", srcFilename, dstFilename);
}
}
int binaryCopy(const char *srcFilename, const char *dstFilename, bool overwrite)
{
//arbitrary odd size so we can make sure we handle a partial buffer.
//assuming the code tests successfully I'd use something like 64 * 1024.
unsigned char buffer[7777] = { 0 };
FILE *src = fopen(srcFilename, "rb");
if (!src)
{
//Error, source file could not be opened
return -1;
}
//open for reading to check for existence
FILE *dst = fopen(dstFilename, "rb");
if (dst)
{
if (!overwrite)
{
//Error, dest file exists and we can't overwrite it
fclose(src);
fclose(dst);
return -2;
}
//reopen dst it for writing
if (!freopen(dstFilename, "wb", dst))
{
fclose(src);
fclose(dst);
dst = NULL;
}
}
else
{
//it didn't exist, create it.
dst = fopen(dstFilename, "wb");
}
if (!dst)
{
//Error, dest file couldn't be opened
fclose(src);
return -3;
}
//Get the size of the source file for comparison with what we read and write.
fseek(src, 0, SEEK_END);
size_t srcSize = ftell(src);
fseek(src, 0, SEEK_SET);
size_t totalRead = 0;
size_t totalWritten = 0;
size_t bytesRead = 0;
while (bytesRead = fread(buffer, 1, sizeof(buffer), src))
{
totalRead += bytesRead;
totalWritten += fwrite(buffer, 1, bytesRead, dst);
}
fclose(dst);
fclose(src);
if (totalRead != srcSize)
{
//src read error
return -4;
}
if (totalWritten != srcSize)
{
//dst write error
return -5;
}
return 0;
}
int main()
{
srand((unsigned)time(0));
createTestFiles(src_pattern);
for (size_t i = TEST_FILE_MIN; i <= TEST_FILE_MAX; i *= 2)
{
char srcName[256];
char dstName[256];
sprintf(srcName, src_pattern, i);
sprintf(dstName, dst_pattern, i);
//use my copy to create dest file
if (binaryCopy(srcName, dstName, true) != 0)
{
printf("File: '%s' failed initial copy.", srcName);
}
originalBinaryCopy(srcName, dstName);
if (binaryCopy(srcName, dstName, true) != 0)
{
printf("File: '%s' failed overwrite copy.", srcName);
}
if (binaryCopy(srcName, dstName, false) == 0)
{
printf("File: '%s' succeeded when file exists and overwrite was not set.", srcName);
}
//If compare succeeds delete the files, otherwise leave them for external comparison and print an error.
if (comp(srcName, dstName) == 0)
{
if (remove(srcName) != 0)
{
perror("Could not remove src.");
}
if (remove(dstName) != 0)
{
perror("Could not remove dst.");
}
}
else
{
printf("File: '%s' did not compare equal to '%s'.", srcName, dstName);
}
}
return 0;
}
Hopefully this gives you something to experiment with to make sure your copier is as good as it can be. Also worth noting, I would not distinguish copying text/binary files. Files are files and if your goal is to copy them then you should always do it in binary mode so the copy is identical. On operating systems other than Windows it wouldn't matter, but on Windows there are a number of pitfalls you can run into in text mode. Best to avoid those completely if you can.
Good luck!
The most probable cause for your observation is the file size is not a multiple of BYTE_SIZE: fread(buf, BYTE_SIZE, size / BYTE_SIZE , src); reads a multiple of BYTE_SIZE and the fwrite call writes the bytes read.
If BYTE_SIZE is 4, as the type int* buf = 0; seems to indicate, and if the source file has 3 more bytes than a multiple of 4, your observations would be fully explained.
You can correct the problem by making buf an unsigned char * and changing the code to:
elements = fread(buf, 1, size , src);
fwrite(buf, 1, elements, dst);
Note also that there is no need to open the files in update mode (the + in the mode string), errors and not handled explicitly and the fclose() calls are misplaced.
Also it seems incorrect to truncate the destination file if overwrite() returns 0.
Here is a corrected version with better error handling:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int binaryCopy(char *argv[]) {
FILE *src, *dst;
long file_size;
size_t size, size_read, size_written;
int wantOverwrite;
unsigned char *buf;
if ((src = fopen(argv[SRC_POS], "rb")) == NULL) {
printf("cannot open input file %s: %s\n", argv[SRC_POS], strerror(errno));
return -1;
}
wantOverwrite = overwrite();
if (!wantOverwrite) {
fclose(src);
return 0;
}
if ((dst = fopen(argv[DST_POS], "wb")) == NULL) {
printf("cannot open output file %s: %s\n", argv[DST_POS], strerror(errno));
fclose(src);
return -1;
}
fseek(src, 0L, SEEK_END);
file_size = ftell(src);
fseek(src, 0L, SEEK_SET);
size = (size_t)file_size;
if ((long)size != file_size) {
printf("file size too large for a single block: %ld\n", file_size);
fclose(src);
fclose(dst);
return -1;
}
buf = malloc(size);
if (buf == NULL) {
printf("cannot allocate block of %zu bytes\n", size);
fclose(src);
fclose(dst);
return -1;
}
size_read = fread(buf, 1, size, src);
if (size_read != size) {
printf("read error: %zu bytes read out of %zu\n", size_read, size);
}
size_written = fwrite(buf, 1, size_read, dst);
if (size_written != size_read) {
printf("write error: %zu bytes written out of %zu\n", size_written, size_read);
}
if (size_written == size) {
printf("copy completed\n");
}
free(buf);
fclose(dst);
fclose(src);
return 0;
}
#EDIT
Looks like fread function reads more characters than record_size parameter ;x
PICTURE
I've got 2 functions which sort file(bubble sort) by records(key is first character). The first one is using system functions(read, write, etc.) and secong is using library functions(fread, fwrite, etc.). For small record_size parameter both works well but e.g for record_size = 5000 only sys_sort works properly. File sorted by lib_sort has less lines and different lengths. Why? I don't know what is the problem.
void lib_sort(const char *filename, long long int record_size, long long int num_of_lines) {
record_size++; // '\n' char at the end of line
FILE *file;
if (!(file = fopen(filename, "r+"))) {
printf("Cannot open %s file.\n", filename);
fclose(file);
exit(EXIT_FAILURE);
}
char *buffer1 = malloc(sizeof(char) * record_size);
char *buffer2 = malloc(sizeof(char) * record_size);
bool flag = true;
while (flag) {
flag = false;
if(fseek(file, 0, SEEK_SET) != 0) {
printf("fseek failed.\n");
}
if((fread(buffer1, sizeof(char), (size_t) record_size, file)) != record_size) {
printf("fread failed.\n");
}
for (int i = 1; i < num_of_lines; ++i) {
if((fread(buffer2, sizeof(char), (size_t) record_size, file)) != record_size) {
printf("fread failed.\n");
}
if (buffer1[0] > buffer2[0]) {
if(fseek(file, record_size * (-2), SEEK_CUR) != 0) {
printf("fseek failed.\n");
}
if((fwrite(buffer2, sizeof(char), (size_t) record_size, file)) != record_size) {
printf("fwrite failed.\n");
}
if((fwrite(buffer1, sizeof(char), (size_t) record_size, file)) != record_size) {
printf("write failed.\n");
}
flag = true;
} else {
char *tmp = buffer2;
buffer2 = buffer1;
buffer1 = tmp;
}
}
num_of_lines--;
}
fclose(file);
free(buffer1);
free(buffer2);
}
And this is the correct one:
void sys_sort(const char *filename, long long int record_size, long long int num_of_records) {
record_size++; // '\n' char at the end of line
int file;
if ((file = open(filename, O_RDWR)) < 0) {
printf("Cannot open %s file.\n", filename);
close(file);
exit(EXIT_FAILURE);
}
char *buffer1 = malloc(sizeof(char) * record_size);
char *buffer2 = malloc(sizeof(char) * record_size);
bool flag = true;
while (flag) {
flag = false;
lseek(file, 0, SEEK_SET);
read(file, buffer1, (size_t) record_size);
for (int i = 1; i < num_of_records; ++i) {
read(file, buffer2, (size_t) record_size);
if (buffer1[0] > buffer2[0]) {
lseek(file, record_size * (-2), SEEK_CUR);
write(file, buffer2, (size_t) record_size);
write(file, buffer1, (size_t) record_size);
flag = true;
} else {
char *tmp = buffer2;
buffer2 = buffer1;
buffer1 = tmp;
}
}
num_of_records--;
}
close(file);
free(buffer1);
free(buffer2);
}
I use ubuntu 16.04 and standard C99
You are using fread() and fwrite() incorrectly.
size_t fread(void *ptr, size_t size, size_t nmemb, FILE * stream );
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
Description
The function fread() reads nmemb elements of data, each size bytes long, from the stream pointed to by stream, storing them at the location given by ptr.
The function fwrite() writes nmemb elements of data, each size bytes long, to the stream pointed to by stream, obtaining them from the location given by ptr.
You are telling fread() and fwrite() that the length of your record or item is 1 byte long and you are asking for 5000 records.
Return Value
On success, fread() and fwrite() return the number of items read or written. This number equals the number of bytes transferred only when size is 1. If an error occurs, or the end of the file is reached, the return value is a short item count (or zero).
Thee parameters you pass to fread() and fwrites() are in the wrong order when in your code you clearly meant the record size (length of the item) is record_size or 5000 in your failing scenario.
You should have written your code to call fread() in this manner instead:
fread(buffer1, (size_t) record_size, 1, file)
and call fwrite() in this manner:
fwrite(buffer2, (size_t) record_size, 1, file)
It should also be noted that fread() and fwrite() works on binary streams. This means that strings are not automatically null terminated and reading will read past newlines and past null \0 bytes. On writes, newlines are not automatically converted to OS supported newlines such as LF on Linux and CRLF on Windows.
For string operations, use fgets() and fputs() instead.
You have forgotten to mention and I have wrongly assumed that you are running on Linux. Based on your comment, you are running on Windows after all. On Windows, fread() and fwrite() will not work correctly when the file is opened in text mode due to translations with OS dependent encoding. You will need to open the file in binary mode.
I put fseek(file, 0, SEEK_CUR); after every fread() and fwrite() functions and that worked for me. I don't know why.
FILE *infp, *outfp;
infp = fopen(argv[2], "r");
int len;
char *text;
fseek(infp, 0, SEEK_END);
len = ftell(infp);
printf("%d\n", len);
if ((text = (char *) malloc(500000000)) == NULL)
{
fprintf(stderr, "Error allocating memory\n");
exit(1);
}
fread(text, len, 1, infp);
text[len] = '\0';
fclose(infp);
printf("Text = %s, Address = %u\n", text, text);
returns
138
Text = , Address = 3794927632
I'm not sure why text isn't printing anything. Am I using fread wrong somehow?
You need to reset the file position with rewind() or fseek(3) like this
FILE *infp;
FILE *outfp;
int length;
char *text;
if ((infp = fopen(argv[2], "r")) == NULL)
{
fprintf(stderr, "Error openning `%s'\n", argv[2]);
return -1;
}
fseek(infp, 0L, SEEK_END);
len = ftell(infp);
/* reset position */
fseek(infp, 0L, SEEK_SET); /* essentially rewind(infp); */
printf("%d\n", length);
if ((text = malloc(length + 1)) == NULL)
{
fprintf(stderr, "Error allocating memory\n");
return -1;
}
if (fread(text, 1, length, infp) == length)
{
text[length] = '\0';
printf("Text = %s, Address = %u\n", text, text);
free(text); /* never forget to `free' */
}
else
{
free(text);
text = NULL:
}
fclose(infp);
You also should
Check the return value of fopen(), you never check if the file was actually opened which I think is the main problem.
Allocate only the necessary space.
Ensure that fread() didn't fail.
Swap fread(3)'s size parameters, first is the element size and then the number of elements
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
and the return value should be equal to nmemb, read the manual page at the link above.
After
fseek(infp, 0, SEEK_END);
infp points to the end of the file. You need to rewind the file.
rewind(infp);
See http://www.cplusplus.com/reference/cstdio/rewind/ for additional info.
I am using Visual Studio 2010 and in the following code snippet there occurs an exception at the if statement after fseek.
int load_filenew(char *filename, char **buffer)
{
int size = 0;
FILE *fp = 0;
fp = fopen(filename, "rb");
if (!fp)
{
printf(" fopen failed.\n");
return 1;
}
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (size)
{
if (*buffer)
{
free(*buffer);
}
*buffer = 0;
*buffer = (char *)malloc(size + 1);
if (!*buffer)
{
printf(" malloc failed.\n");
fclose(fp);
return 3;
}
memset(*buffer, 0, size + 1);
fread(*buffer, size, 1, fp);
(*buffer)[size] = '\0';
}
else
{
fclose(fp);
return 2;
}
fclose(fp);
return 0;
}
This function is called several times in the application but at some times unhandled exception is thrown at following line while loading a file
//exception code
if (size)
{
if (*buffer)
Please help — what could be the possible cause and how to resolve it?
It seems like buffer is probably set to NULL or some other invalid pointer and probably segfaults when you dereference it. It could also be your first call to free if the pointer is invalid. Ideally you need to show us the code that calls this function.
Also keep in mind it's bad form to call a matching malloc and free in different functions. Unless that function has only one purpose, to allocate a new structure or to free an existing one (In other words allocation of any resource should be done in the same function as deallocation of that same resource. The only exception is a function that composes more complex allocations and deallocations).
int load_filenew(char *filename, char **buffer)
{
int size = 0;
FILE *fp = 0;
if(buffer == NULL)
{
return 1;
}
fp = fopen(filename, "rb");
if (!fp)
{
printf(" fopen failed.\n");
return 2;
}
fseek(fp, 0, SEEK_END);
size = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (size)
{
*buffer = (char *)malloc(size + 1);
if (!*buffer)
{
printf(" malloc failed.\n");
fclose(fp);
return 3;
}
memset(*buffer, 0, size + 1);
fread(*buffer, size, 1, fp);
(*buffer)[size] = '\0';
}
else
{
fclose(fp);
return 3;
}
fclose(fp);
return 0;
}
not solving your problem directly but:
if (*buffer)
{
free(*buffer);
}
*buffer = 0;
*buffer = (char *)malloc(size + 1);
have you considered using realloc() instead?
p = realloc(*buffer, size + 1);
if ( p != NULL )
{
*buffer = p;
}
What is the simplest way (least error-prone, least lines of code, however you want to interpret it) to open a file in C and read its contents into a string (char*, char[], whatever)?
I tend to just load the entire buffer as a raw memory chunk into memory and do the parsing on my own. That way I have best control over what the standard lib does on multiple platforms.
This is a stub I use for this. you may also want to check the error-codes for fseek, ftell and fread. (omitted for clarity).
char * buffer = 0;
long length;
FILE * f = fopen (filename, "rb");
if (f)
{
fseek (f, 0, SEEK_END);
length = ftell (f);
fseek (f, 0, SEEK_SET);
buffer = malloc (length);
if (buffer)
{
fread (buffer, 1, length, f);
}
fclose (f);
}
if (buffer)
{
// start to process your data / extract strings here...
}
Another, unfortunately highly OS-dependent, solution is memory mapping the file. The benefits generally include performance of the read, and reduced memory use as the applications view and operating systems file cache can actually share the physical memory.
POSIX code would look like this:
int fd = open("filename", O_RDONLY);
int len = lseek(fd, 0, SEEK_END);
void *data = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
Windows on the other hand is little more tricky, and unfortunately I don't have a compiler in front of me to test, but the functionality is provided by CreateFileMapping() and MapViewOfFile().
If "read its contents into a string" means that the file does not contain characters with code 0, you can also use getdelim() function, that either accepts a block of memory and reallocates it if necessary, or just allocates the entire buffer for you, and reads the file into it until it encounters a specified delimiter or end of file. Just pass '\0' as the delimiter to read the entire file.
This function is available in the GNU C Library, http://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getdelim-994
The sample code might look as simple as
char* buffer = NULL;
size_t len;
ssize_t bytes_read = getdelim( &buffer, &len, '\0', fp);
if ( bytes_read != -1) {
/* Success, now the entire file is in the buffer */
If you are reading special files like stdin or a pipe, you are not going to be able to use fstat to get the file size beforehand. Also, if you are reading a binary file fgets is going to lose the string size information because of embedded '\0' characters. Best way to read a file then is to use read and realloc:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main () {
char buf[4096];
ssize_t n;
char *str = NULL;
size_t len = 0;
while (n = read(STDIN_FILENO, buf, sizeof buf)) {
if (n < 0) {
if (errno == EAGAIN)
continue;
perror("read");
break;
}
str = realloc(str, len + n + 1);
memcpy(str + len, buf, n);
len += n;
str[len] = '\0';
}
printf("%.*s\n", len, str);
return 0;
}
Note: This is a modification of the accepted answer above.
Here's a way to do it, complete with error checking.
I've added a size checker to quit when file was bigger than 1 GiB. I did this because the program puts the whole file into a string which may use too much ram and crash a computer. However, if you don't care about that you could just remove it from the code.
#include <stdio.h>
#include <stdlib.h>
#define FILE_OK 0
#define FILE_NOT_EXIST 1
#define FILE_TOO_LARGE 2
#define FILE_READ_ERROR 3
char * c_read_file(const char * f_name, int * err, size_t * f_size) {
char * buffer;
size_t length;
FILE * f = fopen(f_name, "rb");
size_t read_length;
if (f) {
fseek(f, 0, SEEK_END);
length = ftell(f);
fseek(f, 0, SEEK_SET);
// 1 GiB; best not to load a whole large file in one string
if (length > 1073741824) {
*err = FILE_TOO_LARGE;
return NULL;
}
buffer = (char *)malloc(length + 1);
if (length) {
read_length = fread(buffer, 1, length, f);
if (length != read_length) {
free(buffer);
*err = FILE_READ_ERROR;
return NULL;
}
}
fclose(f);
*err = FILE_OK;
buffer[length] = '\0';
*f_size = length;
}
else {
*err = FILE_NOT_EXIST;
return NULL;
}
return buffer;
}
And to check for errors:
int err;
size_t f_size;
char * f_data;
f_data = c_read_file("test.txt", &err, &f_size);
if (err) {
// process error
}
else {
// process data
free(f_data);
}
What is the simplest way (least error-prone, least lines of code, however you want to interpret it) to open a file in C and read its contents into a string ...?
Sadly, even after years, answers are error prone and many lack proper string formation and error checking.
#include <stdio.h>
#include <stdlib.h>
// Read the file into allocated memory.
// Return NULL on error.
char* readfile(FILE *f) {
// f invalid? fseek() fail?
if (f == NULL || fseek(f, 0, SEEK_END)) {
return NULL;
}
long length = ftell(f);
rewind(f);
// Did ftell() fail? Is the length too long?
if (length == -1 || (unsigned long) length >= SIZE_MAX) {
return NULL;
}
// Convert from long to size_t
size_t ulength = (size_t) length;
char *buffer = malloc(ulength + 1);
// Allocation failed? Read incomplete?
if (buffer == NULL || fread(buffer, 1, ulength, f) != ulength) {
free(buffer);
return NULL;
}
buffer[ulength] = '\0'; // Now buffer points to a string
return buffer;
}
Note that if the text file contains null characters, the allocated data will contain all the file data, yet the string will appear to be short. Better code would also return the length information so the caller can handle that.
char* readfile(FILE *f, size_t *ulength_ptr) {
...
if (ulength_ptr) *ulength_ptr == *ulength;
...
}
If the file is text, and you want to get the text line by line, the easiest way is to use fgets().
char buffer[100];
FILE *fp = fopen("filename", "r"); // do not use "rb"
while (fgets(buffer, sizeof(buffer), fp)) {
... do something
}
fclose(fp);
If you're using glib, then you can use g_file_get_contents;
gchar *contents;
GError *err = NULL;
g_file_get_contents ("foo.txt", &contents, NULL, &err);
g_assert ((contents == NULL && err != NULL) || (contents != NULL && err == NULL));
if (err != NULL)
{
// Report error to user, and free error
g_assert (contents == NULL);
fprintf (stderr, "Unable to read file: %s\n", err->message);
g_error_free (err);
}
else
{
// Use file contents
g_assert (contents != NULL);
}
}
Just modified from the accepted answer above.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
char *readFile(char *filename) {
FILE *f = fopen(filename, "rt");
assert(f);
fseek(f, 0, SEEK_END);
long length = ftell(f);
fseek(f, 0, SEEK_SET);
char *buffer = (char *) malloc(length + 1);
buffer[length] = '\0';
fread(buffer, 1, length, f);
fclose(f);
return buffer;
}
int main() {
char *content = readFile("../hello.txt");
printf("%s", content);
}
// Assumes the file exists and will seg. fault otherwise.
const GLchar *load_shader_source(char *filename) {
FILE *file = fopen(filename, "r"); // open
fseek(file, 0L, SEEK_END); // find the end
size_t size = ftell(file); // get the size in bytes
GLchar *shaderSource = calloc(1, size); // allocate enough bytes
rewind(file); // go back to file beginning
fread(shaderSource, size, sizeof(char), file); // read each char into ourblock
fclose(file); // close the stream
return shaderSource;
}
This is a pretty crude solution because nothing is checked against null.
I will add my own version, based on the answers here, just for reference. My code takes into consideration sizeof(char) and adds a few comments to it.
// Open the file in read mode.
FILE *file = fopen(file_name, "r");
// Check if there was an error.
if (file == NULL) {
fprintf(stderr, "Error: Can't open file '%s'.", file_name);
exit(EXIT_FAILURE);
}
// Get the file length
fseek(file, 0, SEEK_END);
long length = ftell(file);
fseek(file, 0, SEEK_SET);
// Create the string for the file contents.
char *buffer = malloc(sizeof(char) * (length + 1));
buffer[length] = '\0';
// Set the contents of the string.
fread(buffer, sizeof(char), length, file);
// Close the file.
fclose(file);
// Do something with the data.
// ...
// Free the allocated string space.
free(buffer);
easy and neat(assuming contents in the file are less than 10000):
void read_whole_file(char fileName[1000], char buffer[10000])
{
FILE * file = fopen(fileName, "r");
if(file == NULL)
{
puts("File not found");
exit(1);
}
char c;
int idx=0;
while (fscanf(file , "%c" ,&c) == 1)
{
buffer[idx] = c;
idx++;
}
buffer[idx] = 0;
}