[C Series 12] Reading and Writing Data via Standard Input and Files

한국어 버전

Now that you can organize code with headers and source files, it is time to connect your program to the outside world. In C, stdio.h provides the core facilities for standard input/output and file handling. In this lesson we will practice console input (scanf, fgets) and file reading/writing (fopen, fgets, fprintf, fclose) together.

New Terms in This Post

  1. Standard input: The default input stream a program reads from, usually the keyboard
  2. Standard output: The default output stream a program writes to, usually the terminal
  3. File pointer: A FILE * that stores the position and state of an open file
  4. Buffer: A temporary block of memory that holds data before it is processed

Key Ideas

Study Notes

  • Time required: 70–80 minutes
  • Prerequisites: functions, arrays, pointers, experience splitting headers
  • Goal: read values from standard input, open/close files, safely handle text files

We will focus on four flows:

  1. Understand when to choose scanf versus fgets.
  2. Use fopen to obtain a FILE * and always close it with fclose afterward.
  3. Read a file line by line to accumulate results.
  4. Check error cases and print meaningful messages.

Code Walkthrough

Reading Values from Standard Input

#include <stdio.h>

int main(void) {
    int age;
    double height;

    printf("Enter age and height: ");
    if (scanf("%d %lf", &age, &height) != 2) {
        printf("Invalid input format.\n");
        return 1;
    }

    printf("age = %d, height = %.1f\n", age, height);
    return 0;
}

scanf splits input based on whitespace. It returns the number of items it successfully read, which is why the condition checks != 2 in this example. Always compare that result to catch errors.

Also remember that scanf stops at whitespace and can leave the newline character in the input buffer. If you call fgets immediately afterward, it may read that leftover newline as an empty line.

Reading Whole Lines with fgets

#include <stdio.h>

int main(void) {
    char buffer[32];

    printf("Enter your name: ");
    if (fgets(buffer, sizeof(buffer), stdin) == NULL) {
        printf("Input error\n");
        return 1;
    }

    printf("Name: %s", buffer);
    return 0;
}

fgets reads an entire line, including spaces. Always pass both the array and its size to avoid buffer overflows. The trailing newline may be included, so be aware of it. Unlike scanf, fgets does not return a count. It returns the buffer pointer on success and NULL on failure or end-of-file.

If you want to call fgets after scanf, flush the leftover newline first:

int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
}

stdin stands for “standard input.” For now you can treat it as “the keyboard input channel.”

Opening and Writing to a File

#include <stdio.h>

int main(void) {
    FILE *fp = fopen("report.txt", "w");
    if (fp == NULL) {
        printf("Unable to open file.\n");
        return 1;
    }

    fprintf(fp, "C study report\n");
    fprintf(fp, "score = %d\n", 92);

    fclose(fp);
    printf("Created report.txt\n");
    return 0;
}

Mode "w" creates the file or overwrites existing contents. Always check whether the file pointer is NULL, and always call fclose so buffered data is actually written. In real projects you might combine this with perror("fopen") to display the operating system’s error cause.

Reading a File and Computing Totals

#include <stdio.h>

int main(void) {
    FILE *fp = fopen("scores.txt", "r");
    int value;
    int total = 0;
    int count = 0;

    if (fp == NULL) {
        printf("Cannot open scores.txt.\n");
        return 1;
    }

    while (fscanf(fp, "%d", &value) == 1) {
        total += value;
        count++;
    }

    fclose(fp);

    if (count > 0) {
        printf("Average = %.1f\n", (double)total / count);
    } else {
        printf("No data.\n");
    }

    return 0;
}

fscanf uses the same format specifiers as scanf but reads from a file. Check the return value every time. After the loop, guard against dividing by zero. This pattern works well when the file truly contains only integers—if a non-numeric token appears, the loop stops at that point.

Processing Files Line by Line

#include <stdio.h>

int main(void) {
    FILE *fp = fopen("names.txt", "r");
    char line[64];
    int line_number = 1;

    if (fp == NULL) {
        printf("Cannot open names.txt.\n");
        return 1;
    }

    while (fgets(line, sizeof(line), fp) != NULL) {
        printf("%d: %s", line_number, line);
        line_number++;
    }

    fclose(fp);
    return 0;
}

This example prints each line exactly as read, newline included. When fgets returns NULL, you’ve hit EOF or an error, so exit the loop.

Why It Matters

I/O is the bridge between your program and its users or other systems. Standard input/output lets you exchange test data quickly, and file I/O enables basic logging, configuration, and reporting. Managing FILE * properly builds the habit of “release every resource you acquire,” which later applies to dynamic memory, sockets, and more.

Practice in CodeSandbox

The sandbox below uses CodeSandbox's Universal starter. For C, the key learning loop is still compile and run in the terminal, so recreate the lesson code as a source file and repeat that cycle directly.

Live Practice

C Practice Sandbox

CodeSandbox

Run the starter project in CodeSandbox, compare it with the lesson code, and keep experimenting.

Universal starterCterminal
  1. Fork the starter and create a practice file such as hello.c
  2. Paste in the lesson code and compile it if clang or gcc is available
  3. Edit the code, rebuild it, and compare the new output

For C, the terminal build loop matters more than a browser preview. Compiler availability can vary by environment, so first confirm that clang or gcc is present in the Universal starter.

Practice

  • Try it: Run each scanf, fgets, fprintf, and fscanf example and log how the data flows.
  • Extend it: Ask the user for a filename, read the scores inside, and compute the average dynamically.
  • Debug it: Remove fclose on purpose and explain why the file may remain empty.
  • Done when: You can explain the difference between standard input and file I/O, and recite the order for obtaining and closing a FILE *.

Wrap-up

We practiced reading and writing data through both the console and files. Next we will borrow memory at runtime with dynamic allocation so we can handle inputs whose sizes are unknown beforehand.

💬 댓글

이 글에 대한 의견을 남겨주세요