11308:完整读取一行
题目
从当前流位置读取一整行,动态增长存储,并区分成功、文件末尾、读取失败、分配失败和参数无效。
解析
不能把 EOF 一律解释成正常结束。fgetc 返回 EOF 后,先用 ferror 判断是否发生读取错误;只有没有读到任何字节且确实到达文件末尾时,才返回 READ_LINE_END。
存储容量按倍数增长,使连续追加的总搬移成本保持线性。每次为新字节和结尾空字符预留空间前,都先检查长度加法;realloc 的结果也先由临时指针接收。直到整行读取成功才写入两个输出对象,因此其他返回状态不会破坏调用方原有结果。
解析
c
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
typedef enum {
READ_LINE_OK,
READ_LINE_END,
READ_LINE_IO_ERROR,
READ_LINE_NO_MEMORY,
READ_LINE_INVALID
} ReadLineResult;
static bool reserve_line(
char **data,
size_t *capacity,
size_t minimum
) {
if (minimum <= *capacity) {
return true;
}
size_t next = *capacity == 0 ? 64 : *capacity;
while (next < minimum) {
if (next > SIZE_MAX / 2) {
next = minimum;
break;
}
next *= 2;
}
char *replacement = realloc(*data, next);
if (replacement == NULL) {
return false;
}
*data = replacement;
*capacity = next;
return true;
}
ReadLineResult read_line(
FILE *stream,
char **out_line,
size_t *out_length
) {
if (stream == NULL ||
out_line == NULL ||
out_length == NULL) {
return READ_LINE_INVALID;
}
char *line = NULL;
size_t length = 0;
size_t capacity = 0;
for (;;) {
int ch = fgetc(stream);
if (ch == EOF) {
if (ferror(stream)) {
free(line);
return READ_LINE_IO_ERROR;
}
if (length == 0) {
free(line);
return READ_LINE_END;
}
break;
}
if (ch == '\n') {
break;
}
if (length == SIZE_MAX - 1) {
free(line);
return READ_LINE_NO_MEMORY;
}
if (!reserve_line(
&line,
&capacity,
length + 2
)) {
free(line);
return READ_LINE_NO_MEMORY;
}
line[length++] = (char)(unsigned char)ch;
}
if (!reserve_line(&line, &capacity, length + 1)) {
free(line);
return READ_LINE_NO_MEMORY;
}
line[length] = '\0';
*out_line = line;
*out_length = length;
return READ_LINE_OK;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
来源与改编
题型借鉴 MIT 6.087 Practical Programming in C 的 Assignment 3,采用 CC BY-NC-SA 4.0;本题重新规定了动态存储、流状态和输出保持语义。