11314:合并多个文件
题目
按给定顺序把多个二进制文件复制到调用方提供的输出流,并保证关闭函数自行打开的每个输入流。
解析
先检查全部路径,再开始任何写入,可以避免因为后面的空路径而留下无意义的部分结果。每次只打开一个输入文件,资源数量保持常数。
fread 返回短块时,需要结合 ferror 和 feof 判断原因。fwrite 也可能只写入一部分,因此用内部循环继续写剩余字节;无法继续时记录写错误。无论复制在哪一步结束,copy_one_file 都执行一次 fclose,并且只在没有更早错误时报告关闭错误。
解析
c
#include <stddef.h>
#include <stdio.h>
typedef enum {
MERGE_OK,
MERGE_INVALID,
MERGE_OPEN_ERROR,
MERGE_READ_ERROR,
MERGE_WRITE_ERROR,
MERGE_CLOSE_ERROR
} MergeResult;
static MergeResult copy_one_file(
FILE *output,
const char *path
) {
FILE *input = fopen(path, "rb");
if (input == NULL) {
return MERGE_OPEN_ERROR;
}
MergeResult result = MERGE_OK;
unsigned char buffer[4096];
while (result == MERGE_OK) {
size_t amount = fread(
buffer,
1,
sizeof buffer,
input
);
size_t offset = 0;
while (offset < amount) {
size_t written = fwrite(
buffer + offset,
1,
amount - offset,
output
);
if (written == 0) {
result = MERGE_WRITE_ERROR;
break;
}
offset += written;
}
if (result != MERGE_OK) {
break;
}
if (amount < sizeof buffer) {
if (ferror(input)) {
result = MERGE_READ_ERROR;
} else if (!feof(input)) {
result = MERGE_READ_ERROR;
}
break;
}
}
if (fclose(input) == EOF && result == MERGE_OK) {
result = MERGE_CLOSE_ERROR;
}
return result;
}
MergeResult merge_files(
FILE *output,
const char *const source_paths[],
size_t source_count
) {
if (output == NULL) {
return MERGE_INVALID;
}
if (source_count > 0 && source_paths == NULL) {
return MERGE_INVALID;
}
for (size_t i = 0; i < source_count; ++i) {
if (source_paths[i] == NULL) {
return MERGE_INVALID;
}
}
for (size_t i = 0; i < source_count; ++i) {
MergeResult result = copy_one_file(
output,
source_paths[i]
);
if (result != MERGE_OK) {
return result;
}
}
if (fflush(output) == EOF) {
return MERGE_WRITE_ERROR;
}
return MERGE_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
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
来源与改编
题型借鉴 MIT 6.087 Practical Programming in C 的 Assignment 3,采用 CC BY-NC-SA 4.0;本题改写为逐资源获取、统一释放并区分错误类别的接口。