Truncate a file: Difference between revisions
m {{header|ZX Spectrum Basic}} |
→{{header|C}}: POSIX truncate |
||
Line 4: | Line 4: | ||
=={{header|C}}== |
=={{header|C}}== |
||
===Windows=== |
|||
Windows uses <code>SetEndOfFile()</code> to change the length of a file. This program can truncate or extend a file. It can detect and print errors. |
Windows uses <code>SetEndOfFile()</code> to change the length of a file. This program can truncate or extend a file. It can detect and print errors. |
||
Line 92: | Line 93: | ||
return dotruncate(fn, fp); |
return dotruncate(fn, fp); |
||
}</lang> |
}</lang> |
||
===POSIX=== |
|||
<lang c>#include <unistd.h> |
|||
#include <sys/types.h> |
|||
... |
|||
truncate(filename, length); |
|||
ftruncate(fd, length); |
|||
... |
|||
</lang> |
|||
Both functions have <code>length</code> argument of <code>off_t</code> type. There are about a million possible errors, of interest to this task are <code>EFBIG</code>: size too large; <code>EINVAL</code>: size is negative or too large; <code>EIO</code>: IO error; <code>EACCESS</code> (for <code>truncate</code>): either can't see or can't write to the file; <code>EBADF</code> or <code>EINVAL</code> (for <code>ftruncate</code>): file descriptor is not a file descriptor, or not opened for writing. |
|||
When specifying a new file size larger than current value, the file will be extended and padded with null bytes. |
|||
==Icon and {{header|Unicon}}== |
==Icon and {{header|Unicon}}== |
Revision as of 20:27, 22 July 2011
The task is to demonstrate how to truncate a file to a specific length. This should be implemented as a routine that takes two parameters: the filename and the required file length (in bytes). Truncation can be achieved using system or library calls intended for such a task, if such methods exist, or by creating a temporary file of a reduced size and renaming it, after first deleting the original file, if no other method is available. The file may contain non human readable binary data in an unspecified format, so the routine should be "binary safe", leaving the contents of the untruncated part of the file unchanged. If the specified filename does not exist, or the provided length is not less than the current file length, then the routine should raise an appropriate error condition and exit. On some systems, the provided file truncation facilities might not change the file or may extend the file, if the specified length is greater than the current length of the file. For the purpose of this task, it is permissible for such facilities to be used. Such behaviour should be noted, or optionally a warning message relating to an non change or increase in file size may be implemented.
C
Windows
Windows uses SetEndOfFile()
to change the length of a file. This program can truncate or extend a file. It can detect and print errors.
- If the file does not exist: "The system cannot find the file specified."
- If the length is negative: "An attempt was made to move the file pointer before the beginning of the file."
- If the length is too large: "There is not enough space on the disk."
<lang c>#include <windows.h>
- include <stdio.h>
- include <wchar.h>
/* Print "message: last Win32 error" to stderr. */ void oops(const wchar_t *message) { wchar_t *buf; DWORD error;
buf = NULL; error = GetLastError(); FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, 0, (wchar_t *)&buf, 0, NULL);
if (buf) { fwprintf(stderr, L"%ls: %ls", message, buf); LocalFree(buf); } else { /* FormatMessageW failed. */ fwprintf(stderr, L"%ls: unknown error 0x%x\n", message, error); } }
int dotruncate(wchar_t *fn, LARGE_INTEGER fp) { HANDLE fh;
fh = CreateFileW(fn, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (fh == INVALID_HANDLE_VALUE) { oops(fn); return 1; }
if (SetFilePointerEx(fh, fp, NULL, FILE_BEGIN) == 0 || SetEndOfFile(fh) == 0) { oops(fn); CloseHandle(fh); return 1; }
CloseHandle(fh); return 0; }
/*
* Truncate or extend a file to the given length. */
int main() { LARGE_INTEGER fp; int argc; wchar_t **argv, *fn, junk[2];
/* MinGW never provides wmain(argc, argv). */ argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (argv == NULL) { oops(L"CommandLineToArgvW"); return 1; }
if (argc != 3) { fwprintf(stderr, L"usage: %ls filename length\n", argv[0]); return 1; }
fn = argv[1];
/* fp = argv[2] converted to a LARGE_INTEGER. */ if (swscanf(argv[2], L"%lld%1ls", &fp.QuadPart, &junk) != 1) { fwprintf(stderr, L"%ls: not a number\n", argv[2]); return 1; }
return dotruncate(fn, fp); }</lang>
POSIX
<lang c>#include <unistd.h>
- include <sys/types.h>
...
truncate(filename, length);
ftruncate(fd, length);
...
</lang>
Both functions have length
argument of off_t
type. There are about a million possible errors, of interest to this task are EFBIG
: size too large; EINVAL
: size is negative or too large; EIO
: IO error; EACCESS
(for truncate
): either can't see or can't write to the file; EBADF
or EINVAL
(for ftruncate
): file descriptor is not a file descriptor, or not opened for writing.
When specifying a new file size larger than current value, the file will be extended and padded with null bytes.
Icon and Unicon
Unicon provides the built-in function truncate which can be used to truncate a file. The following line of code truncates filename to newsizeinbytes. The file is opened for both read and write in untranslated mode. <lang Unicon>truncate(f := open(filename, "bu"), newsizeinbytes) & close(f)</lang> Note: The Unicon book incorrectly indicates that truncate doesn't work on Windows.
Java
The built-in function for truncating a file in Java will leave the file unchanged if the specified size is larger than the file. This version expects the source file name and the new size as command line arguments (in that order). <lang java>import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel;
public class TruncFile { public static void main(String[] args) throws IOException{ if(args.length < 2){ System.out.println("Usage: java TruncFile fileName newSize"); return; } //turn on "append" so it doesn't clear the file FileChannel outChan = new FileOutputStream(args[0], true).getChannel(); long newSize = Long.parseLong(args[1]); outChan.truncate(newSize); outChan.close(); } }</lang>
PureBasic
PureBasic has the internal function TruncateFile that cuts the file at the current file position and discards all data that follows. <lang PureBasic>Procedure SetFileSize(File$, length.q)
Protected fh, pos, i If FileSize(File$) < length Debug "File to small, is a directory or does not exist." ProcedureReturn #False Else fh = OpenFile(#PB_Any, File$) FileSeek(fh, length) TruncateFile(fh) CloseFile(fh) EndIf ProcedureReturn #True
EndProcedure</lang>
Ruby
This only works with some platforms. If truncation is not available, then Ruby raises NotImplementedError.
<lang ruby># Open a file for writing, and truncate it to 1234 bytes. File.open("file", "ab") { |f| f.truncate(1234) }
- Trunate a file to 567 bytes.
File.truncate("file", 567)</lang>
Tcl
<lang tcl>package require Tcl 8.5
set f [open "file" r+]; # Truncation is done on channels chan truncate $f 1234; # Truncate at a particular length (in bytes) close $f</lang>
UNIX Shell
The dd(1) command can truncate a file. Because dd(1) would create the file, this example runs ls(1). If the file does not exist, then ls(1) prints an error. If the file exists, then dd(1) truncates the file or prints an error. Unix can extend a file, so there is no error
<lang bash># Truncate a file named "myfile" to 1440 kilobytes. ls myfile >/dev/null &&
dd if=/dev/null of=myfile bs=1 seek=1440k</lang>
Some systems have a truncate(1) command (FreeBSD truncate(1), GNU truncate(1)).
<lang bash># Truncate a file named "myfile" to 1440 kilobytes. truncate -s 1440k myfile</lang>
ZX Spectrum Basic
We can truncate files that were saved as binary:
<lang zxbasic>10 CLEAR 29999 20 PRINT "Start tape to load file to truncate." 30 INPUT "Which file do you want to truncate?";f$ 40 LOAD f$ CODE 30000 50 "Input how many bytes do you want to keep?";n 60 PRINT "Please insert a blank tape for new file." 70 SAVE f$ CODE 30000,n 80 STOP</lang>