- Learn
- Computer Science & IT
- Programming
- Programming in C
- File Input and Output in C
File Input and Output in C
fgets. Store arrays and records in binary files with fread and fwrite, design file formats that read correctly on other machines, and jump to any record with fseek. Throughout, you check every result, explain failures with errno and clean up so that your programs fail safely and report their outcome honestly.IntermediateLesson 7 of 8 in Programming in C Course
What you will learn
- Explain how C programs reach files through buffered streams, open and close files in the right mode, and use the three standard streams with command-line redirection.
- Explain streams, buffering and text versus binary modeDescribe what a stream and a
FILE *are, how buffering and the file position work, and how text mode differs from binary mode on Windows and Unix-like systems. - Open and close files with fopen and fcloseOpen a file in the mode that matches the task, check that it opened, find files by path, and close every stream when the work is done.
- Use stdin, stdout and stderr with redirection and pipesUse the three streams every C program starts with, send results and error messages to the right place, and let users redirect them from the command line.
- Explain streams, buffering and text versus binary mode
- Process text files one character at a time, with formatted reads and writes, and line by line, choosing the safe pattern for each and testing every result.
- Read and write one character at a timeUse fgetc and fputc with an int variable, stop correctly at end of file, and count, copy and transform text character by character.
- Write and read formatted text with fprintf and fscanfWrite numbers and strings to files in a chosen layout, build file names with sprintf, and read formatted data with fscanf while checking how many items it converted.
- Read lines safely with fgetsRead text one line at a time with fgets, handle newlines, long lines and Windows line endings, and parse each line after reading it.
- Read and write one character at a time
- Write and read raw bytes, arrays and structs with fwrite and fread, and design binary file formats that read correctly on other machines.
- Read and write binary data with fread and fwriteUse fread and fwrite with the right element size and count, copy files in blocks, recognize file types by their first bytes, and store arrays and structs.
- Design binary formats that work across machinesExplain how type sizes, byte order and struct padding make raw binary files machine-dependent, and read and write multi-byte fields in a defined format.
- Read and write binary data with fread and fwrite
- Find and set the file position to jump straight to the data you need, and update fixed-size records in place without rewriting the whole file.
- Find and set the file position with ftell, fseek and rewindReport the current position, move to positions relative to the start, the current place or the end, compute record offsets and file sizes, and know the limits for text streams.
- Update fixed-size records in placeOpen a file for both reading and writing, follow the rule for switching between them, and change, add or remove records without corrupting the file.
- Find and set the file position with ftell, fseek and rewind
- Check the result of every file operation, explain failures to the user with errno, and release every resource so that programs fail safely and report success honestly.
- Check every result and tell end of file from errorsKnow how each stdio function signals failure, use feof and ferror after a read loop ends, and catch write errors that only appear when the buffer is written.
- Explain failures with errno, perror and strerrorUse errno to find out why a library call failed, and print clear error messages that name the program, the file and the reason.
- Clean up resources and report the outcomeClose every stream on every path, leave no half-written output behind, and return an exit status that tells the truth about the result.
- Check every result and tell end of file from errors
File Input and Output in C study guide
1Open files as streams and connect programs to their input and output
Files reach a program through streams, which control access, buffering, and position. Choosing a mode can preserve old data, erase it, or force each write to the end, so opening belongs to the design of the program. The three standard streams extend the same interface to terminals, redirected files, and pipes. Understanding their connections lets you write one processing routine that works in several settings and keeps diagnostics separate from useful results.
1.1 Explain streams, buffering and text versus binary mode
A file is stored data; a stream is the channel a running program uses to reach it. The same stream functions can also work with a terminal or pipe. Include <stdio.h> and keep the stream pointer in a variable of type FILE *.
What the stream remembers
The library's FILE object tracks a buffer, the current file position, and end-of-file and error indicators. Pass its pointer to functions such as fgetc and fprintf; do not inspect or change its fields. A read or write advances the position past the data processed. In a binary file beginning with "hello\nworld\n", reading the first six bytes leaves the next read at the w.
Buffering groups small operations into larger transfers. A successful write can mean that data reached a memory buffer, with the operating-system transfer still pending. fflush(fp) sends pending output to the operating system. fclose(fp) does that before releasing the stream. Neither is a promise that data will survive a sudden power loss.
| Buffering mode | When output is transferred |
|---|---|
| Fully buffered | When the buffer fills, or when explicitly flushed or closed |
| Line buffered | Also when a newline is written |
| Unbuffered | As soon as possible |
Ordinary files opened with fopen are fully buffered. Interactive devices can use different buffering. A terminal or pipe does not provide ordinary file positions for jumping backward and forward.
Choose text or binary mode
Text mode handles human-readable lines. Binary mode preserves byte values without text translations. Add b to the opening mode for binary data: "rb" reads it and "wb" writes it. Images, compressed data, and raw records need binary mode even if a text-mode version appears to work on your own computer.
On Linux and macOS, a newline is one LF byte, 0x0A, and the two modes normally behave alike. On Windows, text output translates '\n' into the two bytes CR LF, 0x0D 0x0A. Text input translates CR LF back into one '\n'. Microsoft's text input also recognizes 0x1A, Ctrl+Z, as end of file.
For example, writing "a\nb\nc\n" in text mode produces six bytes on Linux or macOS and nine on Windows. Three letters contribute three bytes; three Windows line endings contribute six. Reading the Windows file in text mode on Windows gives six characters, while binary mode exposes all nine bytes.
Keep the file's bytes separate from its text
A Windows-created line read on Linux may retain "\r\n". Removing only '\n' leaves '\r', which can make a comparison with "yes" fail. A text-stream position can also reflect translation state, so save and restore text positions instead of calculating byte offsets.
C's exact text round-trip guarantee applies to printable characters, tabs, and newlines, with no space immediately before a newline and a newline ending the last line. Other byte sequences belong in binary streams. C bytes have at least eight bits; examples involving 0–255 byte values and common file formats use eight-bit bytes.
Key Takeaways
- A
FILE *gives library functions access to a stream and its state. - Buffered output can fail when it is flushed or closed; check those results.
- Windows text mode translates line endings; binary mode preserves bytes.
- Save text positions; calculate offsets in binary files.
- Use eight-bit byte assumptions explicitly when interpreting common binary formats.
1.2 Open and close files with fopen and fclose
Choose an opening mode before you write any processing code. The mode controls whether the file must exist, whether its contents survive, and where writes go. fopen acts on the file immediately: a successful "w" open erases an existing file even if no later write occurs.
Match the mode to the task
| Mode | Existing file | Missing file | Access |
|---|---|---|---|
"r" | Keeps contents | Fails | Read from start |
"w" | Erases contents | Creates | Write from start |
"a" | Keeps contents | Creates | Write at end |
"r+" | Keeps contents | Fails | Read and write |
"w+" | Erases contents | Creates | Read and write |
"a+" | Keeps contents | Creates | Read anywhere; write at end |
Add b for binary mode; "r+b" and "rb+" mean the same thing. C11 adds exclusive creation with x in write modes, such as "wx" and "wbx": opening fails if the file exists. Use a documented mode string; extra mode letters are not portable.
Check before using the pointer
fopen returns a new stream pointer on success and NULL on failure. Missing files, inaccessible paths, and missing directories can all cause failure.
FILE *fp = fopen("scores.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Cannot open scores.txt\n");
return EXIT_FAILURE;
}
/* Read through fp here. */
if (fclose(fp) == EOF) {
fprintf(stderr, "Cannot close scores.txt\n");
return EXIT_FAILURE;
}
The snippet uses <stdio.h> and <stdlib.h>. Functions such as fgetc, fprintf, and fclose need a valid stream; passing NULL is undefined behavior. fflush(NULL) is a deliberate exception that flushes all output streams.
fclose returns 0 on success and EOF on failure. Once called, the stream is finished, including when closing reports an error. Do not retry fclose on the same pointer or use it for further I/O. Close files promptly: available streams are limited, and C guarantees support for at least eight open streams, including the three standard ones.
Resolve paths from the running program
A relative path such as "data/input.txt" starts at the current working directory, not the source file's folder. Starting the program elsewhere changes what that path names. Forward slashes also work with Microsoft's C library. A backslash in a C string must be doubled, as in "C:\\data\\in.txt"; otherwise an escape such as \n can change the string.
For names supplied on the command line, use int main(int argc, char *argv[]). In a two-name copy command, argc must be 3, argv[1] is the input name, and argv[2] is the output name. Open input first so a misspelled input name does not create or erase output. If output opening fails, close the already-open input. Use distinct input and output files: opening the input first does not protect it if the output name identifies that same file.
Normal return from main or exit closes streams, but explicit closes let the program detect delayed output errors. A crash can lose buffered data; abort does not provide the normal-exit cleanup guarantee.
Key Takeaways
- Opening with
"w"erases an existing file immediately. - Check
fopenforNULLbefore using the stream. - Relative paths start at the current working directory.
- Close each opened stream once, and never reuse it after
fclose. - Open copy input before output, and use distinct files.
- C11 exclusive write modes fail when the destination exists.
1.3 Use stdin, stdout and stderr with redirection and pipes
A C program starts with three open streams: standard input stdin, standard output stdout, and standard error stderr. They usually connect to the terminal, but the shell can connect them to files or other programs. You can pass them directly to stream functions.
Keep results separate from diagnostics
printf writes to stdout; scanf reads from stdin. getchar() reads a character from stdin, and putchar(c) writes one to stdout. puts(s) writes a string to stdout and adds a newline. Their stream forms let the same code work with a file:
void print_total(FILE *out, int total)
{
fprintf(out, "Total: %d\n", total);
}
Calling print_total(stdout, 12) prints a result; passing an opened file pointer directs the same layout to a file. In production code, make the helper report a failed write to its caller.
Write diagnostics with fprintf(stderr, ...). A data-processing program can then produce a clean result file while explaining failures separately. An error printed to stdout can corrupt the data another program expects to read.
Let the shell choose connections
In a Unix-like shell, this command separates all three streams:
./sortnums < numbers.txt > sorted.txt 2> errors.txt
< supplies input, > creates or replaces output, >> appends output, and 2> redirects errors. The redirection operators also work in Windows Command Prompt, where the program name is commonly written as sortnums.exe. A pipe, as in generate | sortnums, sends the first program's standard output to the second program's standard input. It does not carry stderr by default.
A filter can accept an optional input name and otherwise read stdin. Keep track of ownership: close a stream your function opened, but leave a borrowed stream for its owner. For a simple command-line filter, if (in != stdin) identifies the file it opened itself.
Flush prompts before waiting for input
A terminal commonly makes stdout line buffered. A redirected file or pipe commonly makes it fully buffered. stderr is not fully buffered. This explains why output order can change when both streams are sent to one destination.
After printf("Saving..."), the text can remain in the buffer because there is no newline. Use fflush(stdout) and check for EOF when it must appear before the next operation. Flushing sends pending output; it does not read or discard input. Standard C does not define fflush(stdin) as an input-clearing operation. To discard a line, read characters until '\n' or EOF.
If a program prints two lines to stdout and a warning to stderr between them, a terminal will commonly show program order. With > out.txt 2>&1, the warning can reach the file before buffered standard output. Do not use mixed-stream display order as a record of execution order.
Return a usable result to scripts
Return EXIT_SUCCESS or 0 when the complete job succeeds and EXIT_FAILURE when it fails. Both macros are declared in <stdlib.h>. A shell command such as convert in.csv out.txt && echo done runs the second command only after success. An honest exit status matters as much as a useful diagnostic.
Key Takeaways
- Use
stdin,stdout, andstderrwithout opening them. - Send program results to
stdoutand diagnostics tostderr. - Redirection changes connections; a pipe carries standard output to standard input.
- Flush output when a prompt must be visible immediately.
- Return a failure exit status when the requested job fails.
2Read and write text files
Text processing has three useful scales: individual characters, formatted values, and whole lines. The choice changes what the program can validate and how it recovers from bad input. A character loop must separate bytes from end of file; a formatted read must check its assignment count; a line reader must handle capacity and incomplete records. Worked examples show how to preserve the data actually read while keeping later parsing and reporting under control.
2.1 Read and write one character at a time
Character I/O is useful when each character determines what happens next: counting separators, changing case, or recognizing a small text format. fgetc reads one character, and fputc writes one. Both are declared in <stdio.h>.
Keep the return value in an int
fgetc(fp) returns the character as an unsigned char converted to int, or the negative constant EOF when no character is available because input ended or a read failed. On the usual eight-bit-byte systems, valid byte values run from 0 to 255. An int keeps those values separate from EOF.
int c;
while ((c = fgetc(in)) != EOF) {
if (fputc(c, out) == EOF) {
/* Report a write failure and clean up. */
break;
}
}
/* Check ferror(in) before declaring the input complete. */
A plain char can lose this distinction. With signed char, byte 0xFF can become -1 and look like end of file. With unsigned char, the negative EOF value is converted to a byte and can never compare equal to EOF.
Keep both parentheses around the assignment. In c = fgetc(in) != EOF, comparison happens first, so c receives 1 or 0 instead of the character. getc does the same reading job and may be a macro; fputc returns the written character or EOF.
Count successful reads, not attempts
The loop condition must perform the read. feof describes an earlier attempt that reached the end; it cannot predict the next attempt. A loop that checks !feof(in) and then counts unconditionally adds an extra pass at the end.
For the text "hi\nyo\n", six successful reads give six characters and two newline characters. Counting newline characters alone works for newline-terminated lines. The text "red\ngreen\nblue" has two newlines but three lines because the final "blue" is still a line.
To include a final line without a newline, track the last character as well:
size_t lines = 0;
int c, last = '\n';
while ((c = fgetc(in)) != EOF) {
if (c == '\n') ++lines;
last = c;
}
if (last != '\n') ++lines;
Starting last at '\n' makes an empty file count as zero lines. A read error still requires a separate failure path; a partial count is not a completed result.
Transform or look ahead
toupper from <ctype.h> can change letters before writing them. A successful fgetc result is already in the valid input range for toupper, so fputc(toupper(c), out) is safe inside the loop. Arbitrary signed char values need conversion to unsigned char before character-classification functions.
ungetc(c, in) pushes a character back so that the next read returns it again. One character of pushback is guaranteed; check for EOF if pushing back fails. This helps a parser inspect the next character without losing it. A positioning operation discards pushback.
The count describes what the selected stream mode delivers. On Windows, text mode counts a CR LF pair as one '\n'; binary mode exposes both bytes. A character count through a text stream therefore need not equal the stored byte count.
Key Takeaways
- Keep
fgetcresults inintso byte values remain distinct fromEOF. - Test each read result before processing or counting it.
- Count a nonempty final piece without a newline as a line.
- One character of
ungetcpushback is guaranteed; seeking discards it.
2.2 Write and read formatted text with fprintf and fscanf
Formatted I/O converts between C values and readable text. fprintf writes a layout you choose; fscanf reads values that match a format. Their conversion specifications look similar, but input requires pointers to the right destination types.
Write readable values
Use %d for an int, %ld for a long, %zu for a size_t, %s for a string, and %c for a character. Output %f prints a double; a float argument is promoted to double. %% prints a percent sign.
int result = fprintf(fp, "%-8s%6.2f\n", "Oslo", 3.14159);
if (result < 0) {
/* Handle a write failure. */
}
%-8s left-aligns "Oslo" in eight columns; %6.2f right-aligns "3.14" in six. The resulting line is "Oslo 3.14\n", and the return is 15 characters. Field width is a minimum, not an output length limit. Precision also changes stored information: %.2f writes 3.14159 as "3.14", so those extra digits cannot be recovered from the file.
A simple CSV writer can output "Lima,2024,19.5" with a fixed order and commas between fields. Text containing commas, quotation marks, or newlines needs CSV quoting. Enclose such a field in double quotes, and double any quotation mark inside it. A bare %s writer is suitable only when text fields do not contain those characters.
Format into a bounded array
sprintf has no destination-size argument. Formatting "007.jpg" needs eight bytes including the null terminator. Prefer snprintf:
char name[32];
int n = snprintf(name, sizeof name, "%03d.jpg", 7);
if (n < 0 || (size_t)n >= sizeof name) {
/* Formatting failed or the full result did not fit. */
}
The nonnegative return is the length the complete text would need, excluding the null terminator. Equality with the buffer size already means truncation. Check negativity before converting the result to size_t.
Read the expected number of values
char name[20];
int age;
int matched = fscanf(fp, "%19s %d", name, &age);
This call succeeds completely only if it returns 2. The width 19 leaves a byte for the terminator. The array name supplies its address; the integer needs &age. For input, %f needs float *, while %lf needs double *. A mismatch is undefined behavior, not a harmless conversion.
Most conversions skip leading whitespace, including newlines. fscanf therefore does not enforce one record per line, and %s cannot read "New York" as a single value. A scan set such as %19[^,] reads up to a comma; whole-line input is often clearer for records.
On "10 abc 20", %d first stores 10 and returns 1. The next call returns 0 because "abc" does not match; that text remains unread. Testing != EOF repeats the failure forever. Test == 1, then handle invalid data separately from end of input or a stream error. A conversion result is a count of assigned values, not a truth value for the entire file.
Key Takeaways
- Match every format specification to the argument type.
- Bound input
%sto the array size minus one. - Compare
fscanfwith the exact number of assignments expected. snprintfreturns the required full length; a return at least the capacity means truncation.- CSV text fields need quoting when they contain commas, quotes, or newlines.
- Formatted floating-point output may discard precision.
2.3 Read lines safely with fgets
Reading a complete line before parsing it keeps each text record separate. If a record is invalid, you can report its line number and continue with the next one. fgets limits how much it writes, but you must still handle retained newlines and lines larger than the array.
Read into a known capacity
char line[256];
while (fgets(line, sizeof line, fp) != NULL) {
/* Process this successful read. */
}
if (ferror(fp)) {
/* Report an interrupted read. */
}
fgets reads at most one less than the capacity, stops after a newline if it encounters one, and adds a null terminator. It returns the buffer on success. A NULL result means either that no characters were available at end of file or that an error occurred; do not process the buffer after a read error.
sizeof line supplies the array capacity in this scope. In a helper whose parameter is char *line, it is the pointer's size. Pass the actual capacity separately. The old gets function cannot limit input and was removed in C11.
Recognize pieces of long lines
With char buf[8], "temperature\n" arrives as "tempera" and then "ture\n". The first call uses seven bytes for characters and one for the terminator. A successful read without a newline might be a piece of a long line or the final unterminated piece at end of file.
Choose a policy: join pieces into a larger record, process them as a continuous sequence, or reject the record and discard all of its remaining pieces. For a size limit, inspect the next character before rejecting an exactly full buffer. A next newline completes the record; EOF without a stream error completes an unterminated final line; another character means more content. Push back a character only if later code needs to read it again.
Remove only an ending you actually found
After deciding whether the record is complete, remove line endings:
line[strcspn(line, "\r\n")] = '\0';
strcspn from <string.h> finds the first listed character, or the existing terminator if neither occurs. This handles retained CR LF as well as LF. Deleting line[strlen(line) - 1] unconditionally can remove real data from a final line without a newline, and it is invalid for an empty string.
fputs(line, out) writes the string without adding a newline; puts(line) writes to stdout and adds one. A text copy can pass each successful fgets piece to fputs, checking for failure, even when a long line spans several pieces. For exact original bytes, use binary block I/O instead.
Parse and validate separately
sscanf(line, "%d %9s", &qty, item) returns 2 for "7 apples" and 0 for "seven apples". A failed parse leaves no bad token stuck in the input stream because the line was already read. Numeric conversions with sscanf still require representable input; use strtol for strict range checking.
For strtol, clear errno, pass an end pointer, and check for no digits, ERANGE, and unexpected trailing text. On "42abc", the result 42 is only a numeric prefix. For a whole-number field, allow the chosen trailing whitespace, then require the end of the string. Check the destination type's range before narrowing a valid long to int.
Key Takeaways
fgetskeeps a newline when it fits and always reserves space for a terminator on success.- A full buffer without a newline needs a completeness check.
- Pass capacities explicitly to pointer-based helpers.
- Use
strcspnto remove only a line ending that exists. - Read first, then parse and validate each record.
- Check
strtolfor digits, range errors, and trailing data.
3Store and load data in binary files
Binary files transfer object bytes without converting them to readable text. Element sizes and counts determine how complete arrays, records, and partial final blocks are handled. A file shared between machines also needs an explicit representation: field widths, byte order, and layout. Reading a signature is only the beginning of checking that representation; lengths and counts must be validated before the program trusts or allocates space for the remaining data.
3.1 Read and write binary data with fread and fwrite
Binary I/O transfers the bytes of objects without converting them to readable numbers or words. fread and fwrite take an address, an element size, an element count, and a stream. Their return values count complete elements, so choosing the element size determines what you can learn from the result.
Write and read arrays
int a[5] = {1, 2, 3, 4, 5};
if (fwrite(a, sizeof a[0], 5, out) != 5) {
/* Handle a write failure. */
}
On a system with four-byte int, this writes 20 bytes and returns 5. Reading into another five-element array with the same representation can recover the values. Use "wb" and "rb" so text translations cannot alter the bytes.
For a single object, pass its address: fwrite(&rec, sizeof rec, 1, out). For objects reached through a pointer p, use fwrite(p, sizeof *p, count, out). sizeof p measures the pointer, and &p points at the pointer variable itself. Neither expresses the intended data transfer. When element size or count is zero, both functions return zero without transferring data.
Copy exactly the bytes read
unsigned char buf[512];
size_t n;
while ((n = fread(buf, 1, sizeof buf, in)) > 0) {
if (fwrite(buf, 1, n, out) != n) {
/* Handle failure and stop copying. */
break;
}
}
An element size of 1 makes n a byte count. A 1300-byte input supplies 512, 512, and 276 bytes, then zero. The last write must use 276, not the capacity 512. Writing the whole buffer adds 236 bytes left over from earlier reads.
With fread(buf, 512, 1, in), the same input returns 1, 1, then 0. The final 276 bytes are only a partial element; they are not counted, and that partial element's value is indeterminate. Choose byte elements when a partial final block is useful. Choose record-sized elements when incomplete records must be rejected.
A short fread can mean end of file or an error. Check ferror after copying and check fclose on output: a write accepted into a buffer can fail later. A partial header or record must never be treated as a complete object.
Inspect signatures and stored objects
Use unsigned char for raw bytes; on eight-bit-byte systems it holds 0–255 without negative values. JPEG data begins with 0xFF 0xD8. Camera and phone photos often continue with an APPn marker, producing the recovery pattern FF D8 FF E0 through FF D8 FF EF. That pattern is a useful candidate test; validate the format before trusting it. Some JPEGs use a different next segment.
A struct with char name[20] and an int score commonly occupies 24 bytes. A raw write includes all its stored bytes, including padding. A pointer member saves an address, not the pointed-to string. Use an embedded array or write the string's length and characters separately.
Readable text can take more space: "1000000\n" uses eight bytes on Linux and nine in Windows text mode. An explicitly encoded 32-bit integer uses four eight-bit bytes. Raw object storage is compact but ties the file to the producer's representation.
Key Takeaways
freadandfwritereturn complete element counts, not always byte counts.- Use element size 1 when a partial final block matters.
- Write the number read, not the entire buffer capacity.
- A raw pointer member stores an address, not its target data.
- Check short transfers and output closing.
- Treat signatures as candidate identification, then validate the file.
3.2 Design binary formats that work across machines
A binary stream preserves bytes, but two machines can interpret the same bytes differently. A portable format defines what each byte means instead of adopting whatever layout the compiler gives an object.
Specify sizes, order, and layout
Three choices make a raw object dump machine-dependent:
| Choice | What can differ |
|---|---|
| Type size | int can be two or four bytes; long commonly differs between 64-bit Windows and Linux |
| Byte order | The least or most significant byte can come first |
| Struct layout | Padding and member offsets can change |
Little-endian stores the least significant byte first. Big-endian stores the most significant byte first. The value 258, hexadecimal 0x00000102, is 02 01 00 00 in little-endian order and 00 00 01 02 in big-endian order.
A common struct layout for a char followed by an int occupies eight bytes: one byte for the character, three padding bytes, and four for the integer. Padding values are unspecified. Exact-width types from <stdint.h>, such as uint32_t, fix bit widths where provided; they do not fix byte order or remove padding. Common file-format examples assume eight-bit bytes and the availability of those types.
Decode fields explicitly
Read four bytes completely before interpreting them. For a little-endian field:
uint32_t v = (uint32_t)b[0]
| (uint32_t)b[1] << 8
| (uint32_t)b[2] << 16
| (uint32_t)b[3] << 24;
Here b is an unsigned-byte array. Each cast takes place before the shift, so the arithmetic has the required unsigned width. To encode, extract the low eight bits, then the next eight, and so on. Check every output operation. Writing v directly with fwrite would put the host's byte order back into the format.
Define a complete small format
A scores format can use "SCR1" as a four-byte signature, a four-byte little-endian count, and records made of 20 name bytes plus four little-endian score bytes. The signature also identifies the version. The header is eight bytes; each record is exactly 24 bytes.
Five records occupy 128 bytes. Record index 3 begins at byte 80: skip eight header bytes and three complete records. State the name-field encoding and termination or padding policy as part of a real format; a fixed 20-byte name field is not automatically a C string.
Check a header's complete length and signature before using its fields. Then check counts and lengths against reasonable limits and the available data before allocating memory. A plausible count is not enough if later records are missing.
Read the actual format structure
Some PCM WAV files have a simple 44-byte header, with the sample rate at bytes 24–27. Bytes 44 AC 00 00 decode to 44100 samples per second. Other WAV files contain extra chunks, so a general reader follows chunk identifiers and sizes to locate the fmt chunk. Assuming one fixed header layout for every file can read an unrelated field as the rate.
Text such as CSV avoids integer-size, byte-order, and struct-padding problems and is easy to inspect. It still needs a defined field layout, quoting rules, and checked parsing. Choose it when readability and interchange matter more than compact binary representation.
Key Takeaways
- A portable binary format defines field widths, byte order, and offsets.
- Exact-width integer types alone do not make a struct dump portable.
- Cast bytes to the intended unsigned width before shifting.
- Validate the whole header and limit counts before trusting them.
- Follow chunk structure when a format allows extra chunks.
- A fixed-width text field needs an explicit encoding and length policy.
4Move to any position in a file
File positions make direct access possible when records have known locations. Calculating those locations requires attention to headers, record widths, and zero-based indexes. Text positions have stricter rules because translation can change their meaning. Once a stream supports both reading and writing, positioning also controls valid direction changes. Updating one record therefore combines a location calculation with a checked sequence that preserves the surrounding data.
4.1 Find and set the file position with ftell, fseek and rewind
Sequential reads visit data in order. Random access lets a program jump to a known location, such as one record in a large file. Positioning works only when the underlying stream supports it, and the meaning of a position differs between binary and text streams.
Tell and seek
ftell(fp) returns a long position or -1L on failure. For a binary file, a position counts bytes from its start. fseek(fp, offset, whence) moves relative to a selected reference point and returns 0 on success, nonzero on failure.
| Reference | Meaning of offset |
|---|---|
SEEK_SET | From the start |
SEEK_CUR | From the current position |
SEEK_END | From the end |
For example, fseek(fp, -4L, SEEK_END) requests the last four bytes of a binary file on a system supporting end-relative seeks. Check the result before reading. A request on a pipe or terminal, or a request before the start of an ordinary file, can fail.
Calculate a record location
For headerless fixed-size binary records, the zero-based index multiplied by the record width gives the starting offset. With 24-byte records, index 3 starts at byte 72. After reading that entire record, the position is 96.
long offset = 3L * (long)sizeof(struct record);
if (fseek(fp, offset, SEEK_SET) != 0) {
/* Handle the failed move before reading. */
}
This example assumes the file was written with the same struct layout. A specified portable format uses its defined record width instead. Include header bytes when present. The third record counted from 1 is index 2, not index 3. For large indexes, validate the multiplication and ensure the result fits long before seeking.
On mainstream systems, seeking to the end and using ftell can measure a binary file: a 120-byte file with 24-byte records holds five records. Check both calls and restore the position afterward. Standard C does not require a meaningful SEEK_END for every binary stream, so the method depends on that stream's support.
Save text positions without arithmetic
Text mode can translate line endings. A returned text position is information for getting back to a place, not a count to add or subtract. A nonzero text seek uses a value previously returned by ftell for the same file, with SEEK_SET. A zero offset is permitted with any reference point.
rewind(fp) returns to the start and clears both end-of-file and error indicators. It returns no result, so use checked fseek(fp, 0L, SEEK_SET) when moving successfully must be confirmed. A successful fseek clears end of file and discards ungetc pushback, but leaves an existing error indicator set.
Keep a position token
fgetpos(fp, &pos) saves a position in fpos_t; fsetpos(fp, &pos) restores it. Check both for nonzero failure. Treat fpos_t as an opaque token, with no arithmetic. It can hold position information beyond a simple byte count.
fseek and ftell use long, which is commonly 32 bits on Windows and limits representable positions to about 2 GiB. A position token avoids tying the interface to long, but the supported file size still depends on the implementation and configuration.
Key Takeaways
- Check
fseekfor nonzero andftellfor-1L. - A record offset includes the header and all earlier records.
- Use zero-based indexes consistently.
- Restore text positions with saved tokens, without offset arithmetic.
rewindclears both indicators; successfulfseekclears end of file.- Positioning requires a seekable stream and representable offsets.
4.2 Update fixed-size records in place
An update stream reads and writes the same file. Use "r+b" to preserve an existing binary file while changing its records. "w+b" erases the old file immediately, and append modes force every write to the end.
Make direction changes valid
An update stream has two switching rules:
| Change | Required action between operations |
|---|---|
| Writing to reading | Flush output or perform a positioning operation |
| Reading to writing | Perform a positioning operation, unless the read encountered end of file |
Positioning functions include fseek, fsetpos, and rewind. Check functions that report a result before continuing. A direction change without the required action has undefined behavior; you cannot predict that it will merely affect the next record.
Update one complete record
Suppose a headerless file contains five 24-byte records in the same representation as struct record. To change index 3:
- Open with
"r+b"and check the pointer. - Seek to byte 72 and check success.
- Read exactly one complete record.
- Change its
scoremember to 99. - Seek back to the record's start.
- Write exactly one record and check the close result.
The key middle operations are:
if (fread(&rec, sizeof rec, 1, fp) != 1) {
/* Report the incomplete input, then clean up. */
goto cleanup;
}
rec.score = 99;
if (fseek(fp, -(long)sizeof rec, SEEK_CUR) != 0) {
/* Report the failed seek, then clean up. */
goto cleanup;
}
if (fwrite(&rec, sizeof rec, 1, fp) != 1) {
/* Report the failed write, then clean up. */
goto cleanup;
}
The enclosing function supplies a cleanup label that closes its stream and returns failure on these branches. After the read, the position is 96. Moving back 24 bytes both selects the original record and permits the read-to-write switch. A successful replacement leaves the file at 120 bytes with five records. Restoring a saved start position is another valid way to perform the update.
Decide when to rewrite
Appending a 24-byte record grows that file to 144 bytes. Seeking to the beginning of an "ab" or "a+b" stream cannot turn its next write into an overwrite; the write still goes to the current end.
An in-place replacement must fit the space it replaces. Changing "Bo,7" to "Bo,9" fits. Writing "Bo,12" over "Bo,7\n" consumes the newline and joins the following text onto the record. To grow or shrink records, write a corrected new file. Standard C has no stream function that removes a range of bytes from the middle of a file. Deletion can instead copy all surviving records or mark a fixed-size record as deleted.
Keep replacement recoverable
Check remove and rename; both return 0 on success. Whether rename replaces an existing destination is implementation-defined. Linux and macOS support replacement; Microsoft's C library fails when the destination exists.
A portable recovery plan writes and closes the new file, renames the original to a distinct backup name, and renames the new file to the original name. If installation fails, keep the backup available for restoration and report where it is. Delete the backup only after installation succeeds. This sequence supports recovery; it is not a portable atomic transaction or a guarantee against power failure.
Key Takeaways
- Use
"r+b"to update an existing file without truncating it. - Follow the read/write switching rule even when the intended position seems obvious.
- Seek back after reading a record before replacing it.
- Append-mode writes always go to the end.
- Rewrite data when its length changes.
- Check each replacement step and preserve a recoverable original.
5Detect, report and recover from file errors
A file operation can fail after opening succeeded, and buffered output can fail only at closing. Reliable programs read return values, distinguish invalid data from access problems, preserve useful error information, and clean up on every path. These tasks work together: the diagnostic should explain the original failure, while the final status must also reflect failures found during cleanup. A complete copy program shows how those decisions fit into ordinary C control flow.
5.1 Check every result and tell end of file from errors
A file can open successfully and still fail during reading, writing, seeking, or closing. A robust program checks the result of the operation it actually attempted. It distinguishes a completed read from interrupted input and distinguishes malformed data from a failing stream.
Learn each failure signal
| Operation | Signal to handle |
|---|---|
fopen, fgets | NULL |
fgetc, fputc, fflush, fclose | EOF |
fprintf | A negative result |
fputs | EOF |
fread, fwrite | Fewer complete elements than requested |
fscanf | Fewer assignments than expected, or EOF |
fseek, fgetpos, fsetpos | A nonzero result |
ftell | -1L |
rewind, clearerr, and perror return no value. If successful movement to the start must be checked, use fseek instead of relying on a result from rewind.
Inspect the stream after a read stops
The stream keeps separate end-of-file and error indicators. feof(fp) tests the first; ferror(fp) tests the second. Each returns nonzero if its indicator is set.
while (fgets(line, sizeof line, fp) != NULL) {
/* Process successful input. */
}
if (ferror(fp)) {
fprintf(stderr, "Input could not be completed\n");
/* Return failure after cleanup. */
}
The return from fgets alone does not distinguish the two stopping reasons. Similarly, fgetc returns EOF and fread returns a short count for either end of file or read failure. Do not test feof before the read to predict whether data remains.
Indicators persist. clearerr clears both, as does rewind. Successful fseek or fsetpos clears end of file, but not an existing error. Clearing an indicator acknowledges it; it does not reconnect a removed drive or repair a damaged file. Retry only when the cause has been addressed.
Separate invalid data from failed access
If fscanf(fp, "%d", &value) returns 0 on "abc", conversion failed before any assignment. Neither end of file nor a read error is required. Report an invalid field, preferably with a line number. A message about the disk would describe the wrong problem.
If a required header is read as one element and fread returns 0, the header is incomplete. Do not inspect that partially filled object. If ferror is set, report an input error; if the file ended, report truncated data. An empty file and a partial header can produce the same element count, so the expected format supplies the requirement that the header must exist.
Catch delayed output failures
A disk can fill after the program has opened its output. Buffered writes can appear successful until fflush or fclose actually transfers pending bytes. Check the final close and let it turn an otherwise successful job into failure. Do not close the same stream again afterward.
At a terminal, Ctrl+D at the start of a line on Linux or macOS, or Ctrl+Z then Enter on Windows, commonly signals end of input. clearerr(stdin) clears the resulting indicator if the program needs another attempt. The terminal action and a read error remain different causes.
Key Takeaways
- Check each function against its own documented return value.
- Read first, then use stream indicators to identify the stopping reason.
- A format mismatch is invalid data, not necessarily a stream error.
- Reject incomplete required headers and records.
- A delayed error at output close must make the job fail.
5.2 Explain failures with errno, perror and strerror
A return value tells you that an operation failed. An error code can help explain why. Use errno only in the context of the operation's documented behavior, and preserve it before unrelated library calls can change it.
Let the operation establish failure
errno is a modifiable int expression from <errno.h>, with thread-local storage duration in C11. It starts at zero. Library calls do not reset it to zero, and a successful call can leave or set a nonzero value. Testing errno after a successful fopen therefore does not tell you that the open failed.
On POSIX systems, a failed fopen sets an error code. Microsoft's C library also provides file error codes. Standard C does not require fopen to set errno. Portable reporting can clear it before the open and use a general message if no code was supplied:
errno = 0;
FILE *fp = fopen(name, "r");
if (fp == NULL) {
int saved = errno;
if (saved != 0)
fprintf(stderr, "Cannot open %s: %s\n",
name, strerror(saved));
else
fprintf(stderr, "Cannot open %s\n", name);
return EXIT_FAILURE;
}
This snippet also needs <stdio.h>, <stdlib.h>, and <string.h>. Saving the code immediately keeps later reporting or cleanup calls from replacing the original reason. A saved code is useful even if the eventual message is printed much later.
Choose a message function
perror(label) writes the label, a colon and space, the current error message, and a newline to stderr. A typical missing-file message is "missing.txt: No such file or directory"; exact wording depends on the system.
strerror(code) returns the library's message for a supplied code, so it fits a custom diagnostic naming the program, operation, and file. Do not modify the returned string. Use strerror(saved) after cleanup instead of asking for the new value of errno.
C defines EDOM, ERANGE, and EILSEQ. Common file codes such as ENOENT, EACCES, EEXIST, and ENOSPC are supplied by POSIX and widely available C libraries. Match your code to the interface and platform you target. Do not assume numeric code values or exact English message text are universal.
Handle an ambiguous return value
For some functions, the result alone cannot distinguish success from a range error. strtol can return LONG_MAX as a valid number or as the result of overflow. Clear errno before converting, then inspect the documented range code:
errno = 0;
char *end;
long value = strtol(text, &end, 10);
if (end == text) {
/* No digits were converted. */
} else if (errno == ERANGE) {
/* The value is outside the long range. */
} else {
/* Validate trailing characters and the target range. */
}
A useful diagnostic gives the user something to act on. Name the file and attempted action; preserve the system reason when available. Send it to stderr, leaving stdout available for valid result data.
Key Takeaways
- Use the function result to establish failure before interpreting
errno. - Save
errnoimmediately before reporting or cleanup calls. perroruses currenterrno;strerroraccepts a saved code.- Reset
errnobeforestrtoland checkERANGE. - Include the file and attempted action in a diagnostic.
5.3 Clean up resources and report the outcome
Error handling is complete only when resources are released and the program reports the real outcome. Plan ownership when you open a stream, and make every later path honor that plan.
Give every open one close
If input opening fails, there is no stream to close. If output opening fails after input succeeded, close the input. If copying fails after both opened, close both, check the output close, and keep a failure status. Closing a pointer twice is not extra protection; it uses a finished stream.
Early-return code usually closes resources in reverse order. A shared cleanup label is useful when several failure paths need the same work. Initialize pointers to NULL, keep a failure status by default, and set success only after the processing phase finishes.
The following complete program copies distinct input and output files in binary mode. It uses generic messages because standard C does not require every stdio failure to supply errno.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *in = NULL, *out = NULL;
int status = EXIT_FAILURE;
int output_acquired = 0;
unsigned char buf[512];
size_t n;
if (argc != 3) {
fprintf(stderr, "Usage: %s input output\n", argv[0]);
return EXIT_FAILURE;
}
in = fopen(argv[1], "rb");
if (in == NULL) {
fprintf(stderr, "Cannot open input: %s\n", argv[1]);
goto cleanup;
}
out = fopen(argv[2], "wb");
if (out == NULL) {
fprintf(stderr, "Cannot open output: %s\n", argv[2]);
goto cleanup;
}
output_acquired = 1;
while ((n = fread(buf, 1, sizeof buf, in)) > 0) {
if (fwrite(buf, 1, n, out) != n) {
fprintf(stderr, "Write failed: %s\n", argv[2]);
goto cleanup;
}
}
if (ferror(in)) {
fprintf(stderr, "Read failed: %s\n", argv[1]);
goto cleanup;
}
status = EXIT_SUCCESS;
cleanup:
if (out != NULL && fclose(out) == EOF) {
fprintf(stderr, "Close failed: %s\n", argv[2]);
status = EXIT_FAILURE;
}
if (in != NULL && fclose(in) == EOF) {
fprintf(stderr, "Close failed: %s\n", argv[1]);
status = EXIT_FAILURE;
}
if (output_acquired && status != EXIT_SUCCESS) {
if (remove(argv[2]) != 0)
fprintf(stderr, "Cannot remove incomplete output: %s\n",
argv[2]);
}
return status;
}
The input loop processes only bytes actually read. A short write jumps to cleanup; an input error is checked after the loop. The output close can still change success into failure. Removal is attempted only when this run successfully opened the output, so an opening failure does not trigger deletion of an unrelated file. The distinct-file requirement matters: "wb" would erase input if the two names identified the same file.
Preserve important existing data
Direct output opening cannot undo truncation. To replace valuable data, write under a separate temporary name first. Check every write and final close before renaming. Keep the original under a backup name until the new file is installed; check every rename and remove. If installation fails, report the recoverable file's location instead of deleting it.
A helper should close what it opened and return a success or failure result to its caller. The caller can decide whether to retry or exit. Returning EXIT_FAILURE after any incomplete step lets a script stop before a dependent action, such as deleting the original after a backup.
Key Takeaways
- Initialize stream ownership and keep failure as the default status.
- Close every successfully opened stream exactly once on every path.
- Let a close failure change the final status.
- Remove incomplete output only when this run created or opened it.
- Write replacements separately and preserve the original until installation succeeds.
- Helpers report failures to their callers so callers can choose recovery.
File Input and Output in C examples
1Open a stream without losing existing data
12 steps
- 1IntroductionDecide what must survive
A program is about to save results. Before it opens anything, decide whether old data must survive and whether the file contains readable lines or exact bytes. Those choices determine the mode, and a wrong one can erase old data.
You will trace one write from the program's text through its stream (the library's channel to a file or terminal), then choose where normal results and diagnostics should go (the standard streams). Opening is already an operation on the file, so this decision comes before the first write.
- 2ExplanationReach data through a stream
A stream is the standard library's channel to a file, terminal, or pipe. Keep its handle as a
FILE *from<stdio.h>and pass it to functions such asfprintf.The library object remembers the buffer, position, and status indicators. You do not edit its fields. A file position advances as data is processed; a terminal or pipe cannot be treated as an ordinary file for seeking.
- 3ExampleTrace newlines to stored bytes
Writing
"a\nb\nc\n"supplies three letters and three newline characters. On Linux or macOS, the text file holds six bytes. On Windows, each newline becomes CR LF, giving nine stored bytes.Reading that Windows file in Windows text mode restores six characters. Binary input exposes all nine bytes. The program's text and the stored byte sequence are different views of the same lines.
- 4ConnectionChoose a mode for an image
A byte sequence in a compressed image can contain values that text mode translates. Adding
bselects binary mode and preserves those values.This explains why an image reader that works with
"r"on Linux can fail on Windows. It must use"rb"because it handles a defined sequence of bytes, not lines that the library should translate. - 5ChallengeCheck a character count
On Windows, a text-mode write stores
"m\nn\n". How many bytes does the resulting file contain, and why?---reveal---
It contains six bytes: two letters plus two CR LF pairs. The four characters supplied by the program expand because each newline becomes two stored bytes. In binary mode, those same four supplied bytes would be written without that translation.
- 6ExplanationChoose preservation before access
For an existing file,
"r"permits reading,"w"erases and permits writing, and"a"keeps the data and appends. A+permits both input and output.Use
"r+"to preserve an existing file for updates."w+"still erases it. C11"wx"fails if the file exists, which makes it useful when an existing destination must be protected. - 7TipAcquire and release one stream
After
FILE *fp = fopen(name, "r");, testfp == NULLbefore any stream operation. Failure supplies no stream to process or close.On success, the program owns a stream that needs one
fclose. Check that close too. The stream is finished after the call even when closing fails; retrying a close uses a stream that is no longer valid. - 8ChallengeProtect the old log
An existing log must keep its entries, and a new line must be added. A learner chooses
"w"because the task writes. What mode should be used, and when would"w"destroy the old entries?---reveal---
Use
"a". Mode"w"destroys existing contents as soon as opening succeeds, even before a write. The choice must protect the log during opening, not only during the later processing code. - 9ExplanationSeparate results from explanations
The already-open streams are
stdinfor input,stdoutfor results, andstderrfor diagnostics. A function acceptingFILE *outcan write the same layout to a file or tostdout.A shell can redirect standard output to a file or pipe it to another program. Keep diagnostic text on
stderr, so a machine-readable result does not acquire error messages between its data rows. - 10Key InsightMake buffered text appear
Output may wait in a memory buffer. Terminal standard output is commonly line buffered; redirected output is commonly fully buffered. Standard error is not fully buffered.
A prompt without a newline can need checked
fflush(stdout)before input begins. Flushing does not discard input. To skip an unwanted input line, consume characters through its newline or end of file. - 11ChallengeTrace a filter output
A filter reads standard input, writes results to standard output, and writes errors to standard error. It runs as
filter < raw.txt > clean.txt. Where should a rejected-record message appear, and what status should it return if it cannot complete the job?---reveal---
The diagnostic goes to the inherited error destination, commonly the terminal. Return
EXIT_FAILURE. The result file stays separate from the message, and the shell can detect failure. - 12RecapUse a deliberate stream lifecycle
Choose preservation and text-versus-binary behavior before opening. Check that opening produced a stream, process only through valid pointers, and close each stream you own once.
Standard streams let the shell supply the connections. Separate results from diagnostics and return a status that describes the completed job. These choices let the same small program work interactively, with files, or inside a pipeline.
2Read a text record and validate its contents
15 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionRead at the right scale
- 2ExplanationKeep a character distinct from EOF
- 3ExampleCount only a successful read
- 4ChallengeInclude the final unterminated line
- 5ExampleWrite a controlled layout
- 6TipReserve the string terminator
- 7ExplanationUse the assignment count
- 8ChallengeChoose a bounded formatted name
- 9ExplanationRead a line into known storage
- 10ExampleFollow a line across two reads
- 11TipTrim the ending without trimming data
- 12ExplanationParse the line already read
- 13ChallengeAccept an exact-fit line
- 14ChallengeReject a prefix with an invalid suffix
- 15RecapKeep reading and interpretation separate
3Transfer bytes and define what they mean
12 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionSeparate bytes from values
- 2ExplanationChoose what the return counts
- 3ExampleFollow the final short block
- 4Key InsightDistinguish a record from a byte block
- 5ChallengeUse the successful count
- 6ExplanationDo not let the compiler design the format
- 7ExampleDecode a little-endian value
- 8ExampleLocate a record from its layout
- 9ChallengeCheck the declared amount of data
- 10ConnectionFollow the structure the format allows
- 11ChallengeRequire a complete field before decoding
- 12RecapMake transfer and format agree
4Find a record and replace it safely
13 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionJump to the data you need
- 2ExplanationSelect a reference point
- 3ExampleLocate zero-based record 3
- 4TipRestore text positions as tokens
- 5ChallengeFind the third record
- 6ExplanationPreserve the existing file
- 7ExampleRestore the record start
- 8Key InsightComplete the replacement
- 9ChallengeChoose both the mode and the move
- 10ConnectionRewrite when a record grows
- 11TipKeep installation recoverable
- 12ChallengeInterpret a completed append
- 13RecapPositioning is part of a valid update
5Carry failure through reporting and cleanup
12 steps
Save this lesson to follow every Walkthrough step by step.
- 1IntroductionSuccess includes the final close
- 2ExplanationRead each function result
- 3ExplanationExplain why input stopped
- 4ChallengeReject a truncated header
- 5ExplanationCapture the original reason
- 6ExamplePrint from the saved code
- 7ChallengeChoose the correct diagnostic source
- 8ExplanationTrack which streams were acquired
- 9ExampleLet cleanup revise success
- 10ChallengeDecide a script's next action
- 11Key InsightKeep the original after rename failure
- 12RecapReturn the outcome of the complete job
File Input and Output in C practice questions
Set 1: Choose streams and process complete input10 Practice Questions
Question 1 of 10
Show answerHide answer
"rb" opens an existing file for binary input, preserving its stored bytes. Plain "r" selects text mode, which can translate line endings and recognize Ctrl+Z as end of input on Windows. The write modes do not provide the requested reading access.Question 2 of 10
Show answerHide answer
"w" would erase the earlier log entries as soon as opening succeeds. Mode "r" preserves data but does not permit the new entry to be written; "w+" permits input and output but still erases the file.Question 3 of 10
fgets succeeds on the last line, which contains "maple" with no newline. What does line[strlen(line) - 1] = '\0'; leave?Show answerHide answer
"maple". fgets does not invent a newline when the file has none. Locate CR or LF with strcspn before trimming, so a final line without an ending keeps all of its actual data.Question 4 of 10
0xFF and contains more data. Why can char c; while ((c = fgetc(fp)) != EOF) stop immediately?Show answerHide answer
fgetc returns the byte as a nonnegative int, but storing it in signed eight-bit char can turn 255 into -1. That value can compare equal to the implementation’s EOF, stopping the loop before later bytes. The parentheses are correct here; the narrowing destination type causes the problem.Question 5 of 10
char tag[14]; int count;, what call safely reads one word and an integer into these objects?Show answerHide answer
&count, a pointer to the destination. Width 14 looks like the array size but allows one more input character than the array can safely hold with its terminator.Question 6 of 10
Show answerHide answer
stderr keeps diagnostics separate from the report data sent to stdout. Printing the message on stdout would place it inside the redirected report and could break its expected format. Standard input supplies data to read; it is not the destination for a diagnostic.Question 7 of 10
fwrite(a, 4, 7, fp) writes every requested element. What does it return?Show answerHide answer
fwrite returns the number of complete elements written, so it returns 7. The transfer covers 28 bytes because each element has size 4, but that byte total is not the return value. Byte counts and element counts coincide only when the element size is 1.Question 8 of 10
07 02 00 00. What value does it represent?Show answerHide answer
07 02 as a big-endian pair would give 1794. The host machine’s own byte order does not change the interpretation specified by the file format.Question 9 of 10
Show answerHide answer
SEEK_CUR would add that absolute location to the current position instead of selecting it.Question 10 of 10
fread(&h, 16, 1, fp). It returns 0, ferror(fp) is zero, and feof(fp) is nonzero. How should the reader proceed?Show answerHide answer
ferror does not make its fields safe to interpret.Set 2: Handle text boundaries and binary layouts10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
/work/run and opens "data/items.txt". Which path is resolved?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
producer | consumer > result.txt, where does the producer’s standard error go by default?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
fread(buf, 1, 600, in). What sizes should the successful writes use?Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
char name[8]; receives snprintf(name, sizeof name, "part-%d", 2468), which returns 9. What should the program conclude?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
"cedars\nmaplesx". It repeatedly calls fgets(buf, 8, fp). What record boundaries must it recognize?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
"north\nsouth\neast". How many lines and newline characters does it contain?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
uint32_t, but differ in byte order and struct padding. A format writer dumps struct { char kind; uint32_t count; } with one fwrite. What redesign makes the representation independent of both differences?Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
long p = ftell(fp); and checked. After more reading, what valid call returns to it?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
"r+b" stream seeks to byte 40 and reads one complete record without EOF. A field is changed. What operation before writing both permits the switch and selects that same record?Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Set 3: Update records and preserve trustworthy results10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
"x\ny\n" is written in text mode. How many stored bytes result?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
"64km" is passed to strtol after errno is cleared. The return is 64, errno remains 0, and end points at k. The field must be a whole integer with optional trailing whitespace. What is the correct decision?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
fread(b, 16, 1, fp) with a 16-byte array. What does the second call return, and how may its element be used?Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
"18 pear 26". After one successful fscanf(fp, "%d", &n), the same call returns 0 twice. Why has it not reached 26?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
"r+b" reader seeks to byte 38 and reads one complete record without EOF. It changes a field. What next operation selects the same record and permits the replacement write?Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Set 4: Diagnose failures across a complete file workflow10 Practice Questions
Save this lesson to see every answer and practice every set.
Question 1 of 10
"ready" fails although the visible letters match. What retained character explains this?Show answerHide answer
Save this lesson to see this answer.
Question 2 of 10
totals.txt. What redirection operator is needed?Show answerHide answer
Save this lesson to see this answer.
Question 3 of 10
"a+b" mode, a program seeks to byte 12, reads one record, changes its score to 25, seeks back to byte 12, and writes the record. All calls succeed. What scores do the second and last records hold afterward?Show answerHide answer
Save this lesson to see this answer.
Question 4 of 10
char buf[8], reads return "8642kg\n", then "1234567"; the next checked read confirms ordinary EOF. strtol finds 8642 with trailing kg in the first record and converts all digits in the second, with no range errors. Which records are valid?Show answerHide answer
Save this lesson to see this answer.
Question 5 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 6 of 10
"a\nb". A program uses int c; size_t n = 0, lines = 0; while (c = fgetc(in) != EOF) { ++n; if (c == '\n') ++lines; }. What totals result, and what correction makes it count lines correctly?Show answerHide answer
Save this lesson to see this answer.
Question 7 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 8 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 9 of 10
Show answerHide answer
Save this lesson to see this answer.
Question 10 of 10
Show answerHide answer
Save this lesson to see this answer.
Stuck on your own question? Homework Help
Key Terms
A
Append mode
"a+b" stream can select input but cannot redirect the next write.Assignment count
fscanf record is complete only when the returned assignment count is 2.B
Big-endian order
00 00 01 02 in a big-endian 32-bit field.Binary mode
"rb" so text translation cannot change its compressed bytes.Block copy
Buffering
Byte order
C
Cleanup path
clearerr
CSV quoting
Current working directory
"data/input.txt".E
End pointer
strtol marks the first character after the numeric prefix that the conversion accepted."86cm", the pointer reaches the c, allowing a whole-number field to reject the suffix.End-of-file indicator
EOF
int so a real byte is not mistaken for this marker.ERANGE
errno before strtol so an old range error cannot be mistaken for a new one.errno
Error indicator
fgets returns NULL, ferror can distinguish failed access from ordinary completion.Exclusive creation
"wx" protects an existing report from being erased by the open.Exit status
&& must not run after an incomplete file copy.F
fclose
EOF.fclose(fp) returns, even with an error, that stream cannot be used again.fgetc
int, or returns EOF.fgetc has returned a character successfully.fgets
Field width
%23s leaves space for a terminator in a 24-byte character array.FILE pointer
FILE * that gives library functions access to a stream and its stored state.fp to fgetc after checking that the open returned a valid stream pointer.File position
File signature
Fixed-width integer type
uint32_t field still needs explicit byte encoding to make an interchange file portable.fopen
NULL.fgetc or fclose call.Format specification
%lf requires a double *, while %f requires a float * destination.fpos_t
fgetpos and fsetpos to save and restore all required file-position information.fputc
EOF on failure.fputc after transforming the character it read.fread
fseek
ftell
long, returning -1L on failure; a text position must be treated as a saved location.fwrite
H
Header
I
In-place update
L
Line parsing
Little-endian order
02 01 00 00 represent 258 in a little-endian 32-bit field.M
Matching failure
"oak" can return 0 while the stream’s end-of-file and error indicators remain clear.N
Null terminator
fgets is allowed to read.O
Opening mode
fopen which access is allowed and whether existing contents are kept or erased."a" preserves earlier log entries and places each new write at the end.P
Partial element
fread’s complete-element count.perror
errno value to standard error, followed by a newline.Pipe
Pushback
ungetc to leave it for the next stage.R
Random access
Recoverable replacement
Redirection
2> can save a program’s diagnostic messages in a separate file.rename
Resource ownership
S
snprintf
Standard error
stderr used for diagnostics independently of a program’s normal result stream.stderr while valid data goes to stdout.Standard input
stdin from which a program normally receives its primary input.< values.txt, a filter reads file data through stdin instead of the keyboard.Standard output
stdout used for a program’s normal results and other primary output.stdout into another program’s standard input.strcspn
"\r\n" as the reject set locates an ending without deleting ordinary final data.Stream
strerror
errno.Struct padding
char followed by an int can occupy eight bytes even when their sizes add to five.T
Text mode
U
Update stream
+ mode so that both input and output are permitted under switching rules."r+b" preserves it for read-and-write access.About this lesson
Make your own Study Materials
Turn your notes, slides and documents into Study Guides, Practice Questions, Flashcards and 6 more tools.