Truncate file in C [duplicate] - c

This question already has answers here:
How to truncate a file in C?
(6 answers)
Closed 8 years ago.
I am using fopen fseeko64 ftello64 fclose etc. to operating on a file.
How can I truncate a file? I know that there is no standard way to do this in C. All I want is some way that will work on any win32 platform. I'm using mingw gcc to compile.
Please note: I meant truncate the size of the file to a specified size, not make it 0 size. And using a trick like copy part to another file and delete/rename is not appropriate.

SetEndOfFile()
Get a handle to the file with write access, set the file pointer, then call SetEndOfFile().
-Adam

If you want to truncate the file to zero size, you can fopen with the "w" flag:
FILE *fh = fopen("file.txt","w");
if (fh != NULL) fclose(fh);
For truncating to a specific size in standard C, you can do this with a transfer/rename solution, something like:
FILE *finp = fopen ("inp.txt", "rb"); // should check for NULLs
FILE *fout = fopen ("out.txt", "wb");
size_t sz = 100000; // 100,000 bytes
char *buff = malloc (sz); // should check for NULL
sz = fread (buff, 1, sz, fin); // should check for errors
fwrite (buff, 1, sz, fout);
free (buff);
fclose (fin);
fclose (fout);
rename ("out.txt", "inp.txt); // should check for error
Of course, if you have access to the Win32 headers and libraries (and I believe MinGW gives you this), you can use SetEndOfFile(), since it does it in place, rather than having to create a new file and then rename it.
That means using Windows handle-based file I/O rather than the C FILE*-based but, if you're limiting yourself to Windows anyway, that may not matter. If you want portability on the other hand, you'll need a solution based on standard C, such as the transfer/rename solution above.

For FILE based file operations, use _fileno() and _chsize_s() to change the size of a file.
int changesize(FILE *fp, __int64 size)
{
int filedes = _fileno(fp);
return _chsize_s(filedes, size);
}
A truncate version can be written by validating that the supplied size is less than the current file size, as _chsize_s() will truncate or extend a file's size - see http://msdn.microsoft.com/en-us/library/whx354w1(VS.80).aspx.

If you simply fopen() a file with the "w" argument, it will be truncated.
http://www.cplusplus.com/reference/clibrary/cstdio/fopen.html

As mentioned already, you can use fopen() with the "w" flag like:
FILE *f = fopen("file.txt", "w");
Also, if you already have the file opened, you can use the function freopen(), again with the "w" flag:
FILE *f = fopen("file.txt", "r"); //initial fopen() call
...
f = freopen("file.txt", "w", f); //reopens "file.txt" and truncates it
http://www.cplusplus.com/reference/clibrary/cstdio/freopen.html
EDIT: After seeing you've edited your OP, I won't repost what Pax and Adam Davis has already put. Also, I'll confirm what Pax said, that the MinGW does give you access to the Win32 headers.

Related

in-memory FILE* (without disk access)

We have library which accepts FILE* (CImg). For performance reason we wish to handle data already in memory without accessing a disk. Target platform is windows which unfortunately does not support fmemopen (and funopen)
char* buf = new char[sz];
FILE *fp = fopen("C:\\test.dat", "wb");
int r = setvbuf(fp, buf, _IOFBF, sz);
r = fwrite(src_buf, 1, sz, fp); // Here r contains right size
fseek(fp, 0, SEEK_END);
size_t sz2 = ftell(fp); // sz2 contains right size as well
rewind(fp);
// Test (something like this actually is somewhere deep inside library)
char* read_buf = new char[sz];
r = fread(read_buf, 1, sz, fp); // Zero!
Final fread() can't read anything... Any suggestions?
Possibly because you opened with "wb" (write-only) instead of "wb+" (read + write)
fopen() function
To help further, you can #include errno.h and print out the error code and string.
printf( "errno: %d, error:'%s'\n", errno, strerror( errno ) );
For our specific case we found CImg plugin doing the thing.
As of a sample in an original question - at least MSVC runtime flushes written content to disk itself. So it isn't a valid replace for fmemopen()
https://github.com/Snaipe/fmem is a wrapper for different platform/version specific implementations of in-memory files
It tries in sequence the following implementations:
open_memstream.
fopencookie, with growing dynamic buffer.
funopen, with growing dynamic buffer.
WinAPI temporary memory-backed file.
When no other mean is available, fmem falls back to tmpfile()

How can I obtain a file's size in C? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do you determine the size of a file in C?
How can I obtain a file's size in C? I opened with an application written in C. I would like to know the size, because I want to put the content of the loaded file into a string, which I alloc using malloc(). Just writing malloc(10000*sizeof(char)
You can use the fseek and ftell functions:
FILE* f = fopen("try.txt","rb");
fseek(f, 0, SEEK_END);
printf("size of the file is %ld", ftell(f));
for file size, stat, lstat or fstat will be the right choice.
pleas check stat
int Get_Size( string path )
{
FILE *pFile = NULL;
// get the file stream
fopen_s( &pFile, path.c_str(), "rb" );
// set the file pointer to end of file
fseek( pFile, 0, SEEK_END );
// get the file size
int Size = ftell( pFile );
// return the file pointer to begin of file if you want to read it
rewind( pFile );
// close stream and release buffer
fclose( pFile );
return Size;
}
more answers cplusplus.com
You can position yourself at the end of the file with fseek and use ftell() for that:
FILE *fd;
fd = fopen("filename.txt","rb");
fseek ( fd, 0 , SEEK_END );
int fileSize = ftell(fd);
filesize will contain the size in Bytes.
gekod
I thought there was a standard C function for this, but I couldn't find it.
If your file size is limited, you can use the solution proposed by izomorphius.
If your file can be larger than 2GB then you can use the _filelengthi64 function (see http://msdn.microsoft.com/en-us/library/dfbc2kec(v=vs.80).aspx). Unfortunately, this is a Microsoft/Windows function so it's probably not available for other platforms (although you will probably find similar functions on other platforms).
EDIT: Look at afge2's answer for the standard C function. Unfortunately, I think this is still limited to 2GB.

Why does 'fopen' return a NULL pointer?

I'm working on a simple file splitter/merger program in the C programming language. The problem is, for some reason fopen returns NULL, and because of that, my program is crashing at the fwrite statement. How do I fix this?
Here is the C file:
int SplitFile(char* filename, char* output, size_t size)
{
char current_file_name[256];
int file_count = 0, i = 0;
FILE *file = fopen( filename, "rb" );
printf("split %s into chunks of %d named\n", filename, size);
if (!file)
return E_BAD_SOURCE;
else
{
output = (char *) malloc(size * sizeof(char));
if (output == NULL)
return E_NO_MEMORY;
else
{
int bytes_read = 0;
FILE *outFile;
do
{
bytes_read = fread(output, sizeof(char), size, file );
sprintf(current_file_name, "%s%04lu\n", "part", file_count++);
outFile = fopen (current_file_name, "wb" ); // THIS RETURNS NULL
fwrite(output, sizeof(char), bytes_read, outFile); //CRASHES ON THIS LINE
}
while ( bytes_read > 0 )
;
//fclose(outFile);
}
}
fclose(file);
printf("...\n");
return 0;
}
The proper thing to do is check errno when fopen returns NULL.
I'm going to guess that your problem is that you're trying to write to a filesystem that doesn't allow \n in filenames, but it could be a permissions issue as well.
There are many reasons fopen can return NULL including (but certainly not limited to):
The file doesn't exist
The file is opened in a mode that doesn't allow other accesses
The network is down
The file exists, but you don't have permissions
A file exists with the name you gave, but the current directory of the process is not what you expected so the relative pathname fails to find and open the file.
The way to find out which is responsible is to dig into the errno code.
However just because you resolve this particular error doesn't mean you can assume fopen will never return NULL. When dealing with I/O operations your code simply has to expect failure. It's not possible to predict the success of I/O operations, and they can always fail.
It means that the file might not exist or some permission error occurred while accessing a file such as "Read-Only" or "Write-Protected", so in those cases fopen will return 0 (a NULL pointer). On success it will return a file pointer as a handler.
fp=fopen("c:\\ABC.txt", "r"); cannot be the same as fp=fopen("c:\\abc.txt", "r");.
Use // instead of \\ in a Linux environment.
P.S.: In Linux and Unix-like operating systems file names are case-sensitive.
Is fopen for write return NULL in the first run?
I noticed that in the while you keep open files for write but not closing them.
Try to add fclose(outFile) after fwrite:
outFile = fopen ( current_file_name , "wb" );
fwrite(output, sizeof( char ), bytes_read, outFile);
fclose(outFile)
It is possible you open more files than your OS allows.
In Unix, for fopen(), there is no reason to prepend ./ to a filename passed to fopen().
In my case, i was reading the same file all over again in a while loop and forgot to close it.
I used a function for reading the file and finding a match and the function had a return; statement that terminated the function before doing fclose(fp) :D
The path given for the file is checked from wherever the executable is present.
In my case I was opening the text file in c file when both were present at the same place.
It was continuously giving the error of file not found.
Placed the file in the folder of executable and it started working.
In my case, it was because I was trying to create the file in a directory that does NOT exist.

Does fseek() move the file pointer to the beginning of the file if it was opened in "a+b" mode?

I wish to open a file using the "a+b" mode, i.e. if it does not exist it is created automatically, but if it does I don't want to overwrite it. I want to be able to read and write to the file.
The file is binary, and I want to save records of a specific struct in it. So I want to do fseek() to the record I want and then save the record using fwrite().
The code looks as follows (MyRecord is a typedef to a struct, while FILENAME is a #define to the file's name):
int saveRecord(MyRecord *pRecord, int pos)
{
FILE* file = fopen(FILENAME, "a+b");
if (file == NULL)
{
printf("Unable to open file %s\n", FILENAME);
return 0;
}
fseek(file, pos * sizeof(MyRecord), SEEK_SET);
fwrite(pRecord, sizeof(MyRecord), 1, file);
fclose(file);
return 1;
}
However this code just appends the record to the end of the file, even if I set pos to 0. Why isn't fseek() with SEEK_SET working in append mode?
I know I can simply open it with "r+b" and if it fails open it with "wb", but I want to know why this doesn't work and why fseek() with SEEK_SET is leaving the file pointer at the end. Any references to places where this behaviour is documented appreciated (because I couldn't find any, or I am using the wrong keywords).
That's because in a mode, writing to the FILE* always appends to the end. fseek only sets the read pointer in this mode. This is documented in the C standard, 7.19.5.3 fopen:
Opening a file with append mode ('a' as the first character in the mode argument)
causes all subsequent writes to the file to be forced to the then current end-of-file,
regardless of intervening calls to the fseek function.
Plain C does not have any sane way to achieve what you want. If you're on a POSIX system or anything remotely close, you can use fd=open(FILENAME, O_CREAT|O_RDRW, 0666) and then fdopen(fd, "rb+").
Edit: Another thing you could try, with plain C:
f = fopen(FILENAME, "a+b");
if (!f) /* ... */
tmp = freopen(0, "r+b", f);
if (tmp) f = tmp;
else /* ... */
Use "r+b" mode and fallback to "w+b" if it fails.
The "a+b" mode, allows you to read and append; the "r+b" allows random read and write.
The documentation for fopen describes how the file behaves with the different modes.

What's a binary file and how do I create one?

I would like to create a binary file representing an integer. I think the file should be 4 bytes. I use linux. How to do that?
Another question: How do I assign the content of that file to an integer in C?
In standard C, fopen() allows the mode "wb" to write (and "rb" to read) in binary mode, thus:
#include <stdio.h>
int main() {
/* Create the file */
int x = 1;
FILE *fh = fopen ("file.bin", "wb");
if (fh != NULL) {
fwrite (&x, sizeof (x), 1, fh);
fclose (fh);
}
/* Read the file back in */
x = 7;
fh = fopen ("file.bin", "rb");
if (fh != NULL) {
fread (&x, sizeof (x), 1, fh);
fclose (fh);
}
/* Check that it worked */
printf ("Value is: %d\n", x);
return 0;
}
This outputs:
Value is: 1
From the operating system's point of view, all files are binary files. C (and C++) provide a special "text mode" that does stuff like expanding newline characters to newline/carriage-return pairs (on Windows), but the OS doesn't know about this.
In a C program, to create a file without this special treatment, use the "b" flag of fopen():
FILE * f = fopen("somefile", "wb" );
Open the file for binary read/write. fopen takes a b switch for file access mode parameter - see here
See the fopen page in Wikipedia for the difference between text and binary files as well as a code sample for writing data to a binary file
See man for syscalls open, write and read.

Resources