使用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.
- For the first line: Scan each character until hitting a newline (
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_newlineto fetch and print the first line, then uses the returned position to get the second line. - Calls
get_last_lineto 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_newlinefunction 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
相关产品推荐
相关产品推荐

