You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用read(2)和write(2)系统调用逐字符读文件并处理指定行内容

Alright, let's break down how to solve this problem using raw Unix system calls—no getline() allowed, just manual character scanning with read(2) and write(2). We need to extract the first line (up to the newline), the second line, and the last line of a text file. Here's a complete implementation with explanations:

Approach

The core idea is to work with file descriptors directly, read the file in manageable chunks, and manually track line boundaries:

  • Open the file with open(2) (always handle errors here—file operations fail for all sorts of reasons like missing permissions or non-existent files).
  • Read incrementally: Use a fixed-size buffer (1024 bytes in this example) to avoid loading large files into memory all at once.
  • Track lines:
    • For the first line: Scan each character until hitting a newline (\n), then write everything up to (but not including) that newline.
    • For the second line: Continue scanning from the end of the first line, find the next newline, and write that segment.
    • For the last line: Seek backwards from the end of the file to find the final newline (efficient for large files), then read from that position to the end of the file.

Complete Code

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER_SIZE 1024

// Reads from a starting position until a newline, writes the line if requested, returns position after newline
off_t read_until_newline(int fd, off_t start_pos, int should_write) {
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    off_t current_pos = start_pos;
    int found_newline = 0;

    if (lseek(fd, current_pos, SEEK_SET) == -1) {
        perror("lseek failed");
        return -1;
    }

    while ((bytes_read = read(fd, buffer, BUFFER_SIZE)) > 0) {
        for (ssize_t i = 0; i < bytes_read; i++) {
            if (buffer[i] == '\n') {
                if (should_write) {
                    if (write(STDOUT_FILENO, buffer, i) == -1) {
                        perror("write failed");
                        return -1;
                    }
                    write(STDOUT_FILENO, "\n", 1);
                }
                current_pos += i + 1;
                found_newline = 1;
                break;
            }
        }
        if (found_newline) break;

        if (should_write) {
            if (write(STDOUT_FILENO, buffer, bytes_read) == -1) {
                perror("write failed");
                return -1;
            }
        }
        current_pos += bytes_read;
    }

    if (bytes_read == -1) {
        perror("read failed");
        return -1;
    }

    if (!found_newline && should_write) {
        write(STDOUT_FILENO, "\n", 1);
    }

    return current_pos;
}

// Finds and writes the last line of the file
void get_last_line(int fd) {
    char buffer[BUFFER_SIZE];
    ssize_t bytes_read;
    off_t file_size = lseek(fd, 0, SEEK_END);
    if (file_size == -1) {
        perror("lseek to end failed");
        return;
    }

    off_t current_pos = file_size;
    off_t last_line_start = file_size;
    int found_newline = 0;

    // Seek backwards to find the last newline
    while (current_pos > 0) {
        ssize_t read_size = (current_pos >= BUFFER_SIZE) ? BUFFER_SIZE : current_pos;
        current_pos -= read_size;

        if (lseek(fd, current_pos, SEEK_SET) == -1) {
            perror("lseek backwards failed");
            return;
        }

        bytes_read = read(fd, buffer, read_size);
        if (bytes_read == -1) {
            perror("read backwards failed");
            return;
        }

        // Scan buffer from end to start
        for (ssize_t i = bytes_read - 1; i >= 0; i--) {
            if (buffer[i] == '\n') {
                last_line_start = current_pos + i + 1;
                found_newline = 1;
                break;
            }
        }

        if (found_newline) break;
    }

    // Read and write the last line
    if (lseek(fd, last_line_start, SEEK_SET) == -1) {
        perror("lseek to last line start failed");
        return;
    }

    while ((bytes_read = read(fd, buffer, BUFFER_SIZE)) > 0) {
        if (write(STDOUT_FILENO, buffer, bytes_read) == -1) {
            perror("write last line failed");
            return;
        }
    }
    write(STDOUT_FILENO, "\n", 1);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <file_path>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int fd = open(argv[1], O_RDONLY);
    if (fd == -1) {
        perror("open failed");
        exit(EXIT_FAILURE);
    }

    printf("First line:\n");
    off_t second_line_start = read_until_newline(fd, 0, 1);
    if (second_line_start == -1) {
        close(fd);
        exit(EXIT_FAILURE);
    }

    printf("\nSecond line:\n");
    off_t third_line_start = read_until_newline(fd, second_line_start, 1);
    if (third_line_start == -1) {
        close(fd);
        exit(EXIT_FAILURE);
    }

    printf("\nLast line:\n");
    get_last_line(fd);

    close(fd);
    return EXIT_SUCCESS;
}

Step-by-Step Explanation

  • Helper Functions:
    • read_until_newline: This function handles reading up to a newline from any starting position. It writes the line if requested, and returns the position right after the newline—perfect for chaining to get the next line. It also handles lines longer than the buffer by continuing to read chunks until the newline is found.
    • get_last_line: Instead of reading the entire file front-to-back, this function seeks backwards from the end to find the last newline. This is much more efficient for large files. Once it finds the newline, it reads from that position to the end of the file.
  • Main Logic:
    • Validates input (requires a file path argument).
    • Opens the file in read-only mode with error handling.
    • Uses read_until_newline to fetch and print the first line, then uses the returned position to get the second line.
    • Calls get_last_line to print the final line of the file.
    • Closes the file descriptor to avoid resource leaks.

Edge Cases Handled

  • Empty files: Gracefully skips line outputs.
  • Single-line files: The second line output will be empty, and the last line matches the first.
  • Lines longer than the buffer: The read_until_newline function continues reading chunks until it finds the newline.
  • Last line without a newline: Still correctly identifies and prints the last line (starting from the previous newline or the file's start).

内容的提问来源于stack exchange,提问作者Nicole

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:09:35