2025.12.09 C语言程序设计上机实习六

2025.11.11 C语言程序设计上机实习六

必做题

image-20251209190035516

选做题

题目源文件实验六.pdf

题目1:动态字符串数组的双重排序

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
void SortStrings(char **strs, int n)
{

if (strs == NULL || n <= 0) // 传入失败或字符串小于等于0则退出函数
{
return;
}

for (int i = 0; i < n; i++) // 对第i个字符串进行操作
{
int len = strlen(strs[i]);
// 冒泡排序
for (int j = 0; j < len - 1; j++)
{
for (int k = j + 1; k < len; k++)
{
if (strs[i][j] > strs[i][k])
{
char temp = strs[i][j];
strs[i][j] = strs[i][k];
strs[i][k] = temp;
}
}
}
}

// 冒泡排序
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (strcmp(strs[i], strs[j]) > 0)
{
char *temp = strs[i];
strs[i] = strs[j];
strs[j] = temp;
}
}
}

}

image-20251209204604146

题目2:模拟解析CSV文件

结构体定义

1
2
3
4
5
struct Person {
char name[20];
int age;
char city[20];
};

ParseCSV函数

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
103
104
105
106
107
void ParseCSV(const char *csvData, struct Person **persons, int *count)
{
if (persons == NULL || count == NULL) return;

*persons = NULL;
*count = 0;

// 若 csvData 为空或 NULL,则 return
if (csvData == NULL || csvData[0] == '\0')
{
return;
}

int len = strlen(csvData);
int lines = 0;
char *temp = NULL;
temp = (char *)malloc(sizeof(char) * (len + 1));
if (!temp) free(temp);
if (temp == NULL) return;
memcpy(temp, csvData, len + 1);
for (int i = 0; i < len; i++)
{
if (temp[i] == '\n')
{
lines++;
}
}

if (len > 0 && temp[len - 1] != '\n')
{
lines++;
}

if (lines == 0) return;

*persons = (struct Person *)malloc(sizeof(struct Person) * lines);
if (*persons == NULL) return;

int index = 0;
int status = 1;

char *line;
char *saveptr = NULL;

line = strtok_r(temp, "\n", &saveptr);

while(line && status)
{
char *saveptr2 = NULL;
char *name = strtok_r(line, ",", &saveptr2);
char *ageStr = strtok_r(NULL, ",", &saveptr2);
char *city = strtok_r(NULL, ",", &saveptr2);
char *extra = strtok_r(NULL, ",", &saveptr2);

if (!name || !ageStr || !city || extra != NULL)
{
status = 0;
break;
}

if (strlen(name) == 0 || strlen(name) >= 20)
{
status = 0;
break;
}

strcpy((*persons)[index].name, name);

for (int i = 0; ageStr[i]; i++)
{
if (ageStr[i] < '0' || ageStr[i] > '9')
{
status = 0;
break;
}
}

if (status == 0) break;

(*persons)[index].age = atoi(ageStr);

if (strlen(city) == 0 || strlen(city) >= 20)
{
status = 0;
break;
}
strcpy((*persons)[index].city, city);

index++;
line = strtok_r(NULL, "\n", &saveptr);

}

free(temp);

if(status == 0 || index == 0)
{
free(*persons);
*persons = NULL;
*count = 0;
return;
}

*count = index;

return;
}

AI使用声明:

对于库函数strtok,在本程序中会发生共用缓冲区问题,导致测试例1无法通过。

在《C语言程序设计》附录部分仅介绍了strtok。在AI的帮助下了解到更安全的库函数strtok_r,故更换使用后正常。

若不使用strtok与strtor_r,其他的方法其实还有(包括但不限于)

  • 逐位读取读到“,”再处理
  • 统计“,”在每一行中出现的个数判断格式是否正常

image-20251209204910409

题目3:排序后存储为CSV风格的字符串

1
//未写待补

附:完整代码

TestSortStrings.c

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void SortStrings(char **strs, int n);

// ========== 测试框架 ==========
void run_tests();

// ========== 主函数 ==========
int main() {
run_tests();
return 0;
}

void run_tests()
{
printf("=== 字符串数组双重排序测试 ===\n");
int pass_count = 0;
int total_count = 0;

// 测试用例1:正常情况
printf("\n测试用例1:正常情况\n");
char *strs1[] = {malloc(4), malloc(4), malloc(4)};
strcpy(strs1[0], "cba");
strcpy(strs1[1], "fed");
strcpy(strs1[2], "abc");
SortStrings(strs1, 3);
if (strcmp(strs1[0], "abc") == 0 && strcmp(strs1[1], "abc") == 0 && strcmp(strs1[2], "def") == 0)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败: 实际 [%s, %s, %s]\n", strs1[0], strs1[1], strs1[2]);
}
free(strs1[0]);
free(strs1[1]);
free(strs1[2]);
total_count++;

// 测试用例2:包含空字符串
printf("\n测试用例2:包含空字符串\n");
char *strs2[] = {malloc(1), malloc(5), malloc(6)};
strcpy(strs2[0], "");
strcpy(strs2[1], "dcba");
strcpy(strs2[2], "hello");
SortStrings(strs2, 3);
if (strcmp(strs2[0], "") == 0 && strcmp(strs2[1], "abcd") == 0 && strcmp(strs2[2], "ehllo") == 0)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败\n");
}
free(strs2[0]);
free(strs2[1]);
free(strs2[2]);
total_count++;

// 测试用例3:单字符字符串
printf("\n测试用例3:单字符字符串\n");
char *strs3[] = {malloc(2), malloc(2), malloc(2)};
strcpy(strs3[0], "b");
strcpy(strs3[1], "a");
strcpy(strs3[2], "c");
SortStrings(strs3, 3);
if (strcmp(strs3[0], "a") == 0 && strcmp(strs3[1], "b") == 0 && strcmp(strs3[2], "c") == 0)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败\n");
}
free(strs3[0]);
free(strs3[1]);
free(strs3[2]);
total_count++;

// 测试用例4:大规模动态示例(课堂原题)
printf("\n测试用例4:大规模动态示例(课堂原题)\n");
int m = 26;
char **strs4 = (char**)malloc(m * sizeof(char*));
for (int i = 0; i < m; i++)
{
strs4[i] = (char*)malloc((i + 2) * sizeof(char));
for (int j = 0; j <= i; j++)
{
strs4[i][j] = 'z' - j;
}
strs4[i][i + 1] = '\0';
}

SortStrings(strs4, m);

int valid = 1;
for (int i = 0; i < m; i++)
{
size_t len = strlen(strs4[i]);
for (size_t j = 0; j + 1 < len; j++)
{
if (strs4[i][j] > strs4[i][j + 1])
{
valid = 0;
break;
}
}
if (!valid)
{
break;
}
}

if (valid)
{
for (int i = 0; i < m - 1; i++)
{
if (strcmp(strs4[i], strs4[i + 1]) > 0)
{
valid = 0;
break;
}
}
}

if (valid)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败\n");
}

for (int i = 0; i < m; i++)
{
free(strs4[i]);
}
free(strs4);
total_count++;

// 测试用例5:n为0且strs为NULL
printf("\n测试用例5:n为0且strs为NULL\n");
SortStrings(NULL, 0);
printf(" ✔️ 无异常,通过\n");
pass_count++;
total_count++;

printf("\n=== 测试结果 ===\n");
printf("通过: %d/%d (%.1f%%)\n", pass_count, total_count, (float)pass_count / total_count * 100);
}

void SortStrings(char **strs, int n)
{

if (strs == NULL || n <= 0) // 传入失败或字符串小于等于0则退出函数
{
return;
}

for (int i = 0; i < n; i++) // 对第i个字符串进行操作
{
int len = strlen(strs[i]);
// 冒泡排序
for (int j = 0; j < len - 1; j++)
{
for (int k = j + 1; k < len; k++)
{
if (strs[i][j] > strs[i][k])
{
char temp = strs[i][j];
strs[i][j] = strs[i][k];
strs[i][k] = temp;
}
}
}
}

// 冒泡排序
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
if (strcmp(strs[i], strs[j]) > 0)
{
char *temp = strs[i];
strs[i] = strs[j];
strs[j] = temp;
}
}
}

}

TestParseCSV.c

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Person {
char name[20];
int age;
char city[20];
};


void ParseCSV(const char *csvData, struct Person **persons, int *count);

// ========== 测试框架 ==========
void run_tests();

// ========== 主函数 ==========
int main()
{
run_tests();
return 0;
}

void run_tests()
{
printf("=== CSV 文件解析为结构体测试 ===\n");
int pass_count = 0;
int total_count = 0;

// 测试用例1:正常情况
printf("\n测试用例1:正常情况\n");
const char *csv1 = "Zoe,29,Hangzhou\nAlice,25,Beijing\nBob,30,Shanghai\nCharlie,22,Guangzhou\nDiana,28,Shenzhen";

struct Person *persons1 = NULL;
int count1 = 0;

ParseCSV(csv1, &persons1, &count1);

if (count1 == 5)
{
if (strcmp(persons1[0].name, "Zoe") == 0 && persons1[0].age == 29 && strcmp(persons1[0].city, "Hangzhou") == 0)
{
if (strcmp(persons1[1].name, "Alice") == 0 && persons1[1].age == 25 && strcmp(persons1[1].city, "Beijing") == 0)
{
if (strcmp(persons1[2].name, "Bob") == 0 && persons1[2].age == 30 && strcmp(persons1[2].city, "Shanghai") == 0)
{
if (strcmp(persons1[3].name, "Charlie") == 0 && persons1[3].age == 22 && strcmp(persons1[3].city, "Guangzhou") == 0)
{
if (strcmp(persons1[4].name, "Diana") == 0 && persons1[4].age == 28 && strcmp(persons1[4].city, "Shenzhen") == 0)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败: Diana 行错误\n");
}
}
else
{
printf(" ❌ 失败: Charlie 行错误\n");
}
}
else
{
printf(" ❌ 失败: Bob 行错误\n");
}
}
else
{
printf(" ❌ 失败: Alice 行错误\n");
}
}
else
{
printf(" ❌ 失败: Zoe 行错误\n");
}
}
else
{
printf(" ❌ 失败: 记录数不正确,期望 5,实际 %d\n", count1);
}

free(persons1);
total_count++;

// 测试用例2:空数据
printf("\n测试用例2:空数据\n");
const char *empty = "";
struct Person *persons2 = NULL;
int count2 = 0;

ParseCSV(empty, &persons2, &count2);

if (count2 == 0 && persons2 == NULL)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败\n");
}
total_count++;

// 测试用例3:单行
printf("\n测试用例3:单行数据\n");
const char *single = "Tom,35,Chengdu";
struct Person *persons3 = NULL;
int count3 = 0;

ParseCSV(single, &persons3, &count3);

if (count3 == 1 && strcmp(persons3[0].name, "Tom") == 0 && persons3[0].age == 35 && strcmp(persons3[0].city, "Chengdu") == 0)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败\n");
}

free(persons3);
total_count++;

// 测试用例4:无逗号
printf("\n测试用例4:异常格式(无逗号)\n");
const char *invalid = "John18NewYork\nMary22Paris";
struct Person *persons4 = NULL;
int count4 = 0;

ParseCSV(invalid, &persons4, &count4);
if (count4 == 0 && persons4 == NULL)
{
printf(" ✔️ 通过(格式错误,应返回空)\n");
pass_count++;
}
else
{
printf(" ❌ 失败:应拒绝解析\n");
}
total_count++;

// 测试用例5:字段数量不一致(某行不是3列)
printf("\n测试用例5:字段数量不一致(第二行只有2列)\n");
const char *csv5 = "Zoe,29,Hangzhou\nAlice,25\nBob,30,Shanghai";
struct Person *persons5 = NULL;
int count5 = 0;

ParseCSV(csv5, &persons5, &count5);

if (count5 == 0 && persons5 == NULL)
{
printf(" ✔️ 通过(格式错误,应返回空)\n");
pass_count++;
}
else
{
printf(" ❌ 失败:应拒绝解析\n");
}
total_count++;

// 汇总结果
printf("\n=== 测试结果 ===\n");
printf("通过: %d/%d (%.1f%%)\n", pass_count, total_count, (float)pass_count / total_count * 100);
}


void ParseCSV(const char *csvData, struct Person **persons, int *count)
{
if (persons == NULL || count == NULL) return;

*persons = NULL;
*count = 0;

// 若 csvData 为空或 NULL,则 return
if (csvData == NULL || csvData[0] == '\0')
{
return;
}

int len = strlen(csvData);
int lines = 0;
char *temp = NULL;
temp = (char *)malloc(sizeof(char) * (len + 1));
if (!temp) free(temp);
if (temp == NULL) return;
memcpy(temp, csvData, len + 1);
for (int i = 0; i < len; i++)
{
if (temp[i] == '\n')
{
lines++;
}
}

if (len > 0 && temp[len - 1] != '\n')
{
lines++;
}

if (lines == 0) return;

*persons = (struct Person *)malloc(sizeof(struct Person) * lines);
if (*persons == NULL) return;

int index = 0;
int status = 1;

char *line;
char *saveptr = NULL;

line = strtok_r(temp, "\n", &saveptr);

while(line && status)
{
char *saveptr2 = NULL;
char *name = strtok_r(line, ",", &saveptr2);
char *ageStr = strtok_r(NULL, ",", &saveptr2);
char *city = strtok_r(NULL, ",", &saveptr2);
char *extra = strtok_r(NULL, ",", &saveptr2);

if (!name || !ageStr || !city || extra != NULL)
{
status = 0;
break;
}

if (strlen(name) == 0 || strlen(name) >= 20)
{
status = 0;
break;
}

strcpy((*persons)[index].name, name);

for (int i = 0; ageStr[i]; i++)
{
if (ageStr[i] < '0' || ageStr[i] > '9')
{
status = 0;
break;
}
}

if (status == 0) break;

(*persons)[index].age = atoi(ageStr);

if (strlen(city) == 0 || strlen(city) >= 20)
{
status = 0;
break;
}
strcpy((*persons)[index].city, city);

index++;
line = strtok_r(NULL, "\n", &saveptr);

}

free(temp);

if(status == 0 || index == 0)
{
free(*persons);
*persons = NULL;
*count = 0;
return;
}

*count = index;

return;
}

TestSortSaveAsCSV.c

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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

#define MAX_MALLOC 100

char *SortSaveAsCSV(struct Person *persons, int count);

// =================== 测试框架 ===================
void run_tests();

int main()
{
run_tests();
return 0;
}

void run_tests()
{
printf("=== 排序后存储为CSV风格字符串测试 ===\n");
int pass_count = 0;
int total_count = 0;

// 测试用例1:正常情况(多个人,不同年龄)
printf("\n测试用例1:正常情况\n");
struct Person people1[] = {
(struct Person){"Zoe", 29, "Hangzhou"},
(struct Person){"Alice", 25, "Beijing"},
(struct Person){"Bob", 30, "Shanghai"}
};
char *csv1 = SortSaveAsCSV(people1, 3);

if (csv1 != NULL && strcmp(csv1, "Alice,25,Beijing\nZoe,29,Hangzhou\nBob,30,Shanghai\n") == 0 && malloc_usable_size(csv1) < MAX_MALLOC)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败:输出不正确or内存开辟过大\n");
}
free(csv1);
total_count++;

// 测试用例2:空数组或NULL输入
printf("\n测试用例2:空数组或NULL输入\n");
char *csv2 = SortSaveAsCSV(NULL, 0);
if (strlen(csv2) == 0)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败:应返回空字符串\n");
}
total_count++;

// 测试用例3:单个元素
printf("\n测试用例3:单个元素\n");
struct Person person3 = (struct Person){"Tom", 35, "Chengdu"};
char *csv3 = SortSaveAsCSV(&person3, 1);
if (csv3 != NULL && strcmp(csv3, "Tom,35,Chengdu\n") == 0 && malloc_usable_size(csv3) < MAX_MALLOC)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败:单行输出错误或开辟内存过大\n");
}
free(csv3);
total_count++;

// 测试用例4:相同年龄(稳定排序)
printf("\n测试用例4:相同年龄(稳定排序)\n");
struct Person people4[] = {
(struct Person){"Zoe", 25, "Hangzhou"},
(struct Person){"Alice", 25, "Beijing"},
(struct Person){"Bob", 26, "Shanghai"}
};
char *csv4 = SortSaveAsCSV(people4, 3);
if (csv4 != NULL && strcmp(csv4, "Zoe,25,Hangzhou\nAlice,25,Beijing\nBob,26,Shanghai\n") == 0 && malloc_usable_size(csv4) < MAX_MALLOC)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败:排序不稳定或式格错误or开辟内存过大\n");
}
free(csv4);
total_count++;

// 测试用例5:城市为空字符串
printf("\n测试用例5:城市为空字符串\n");
struct Person people5[] = {
(struct Person){"Zoe", 29, ""},
(struct Person){"Alice", 25, "Beijing"}
};
char *csv5 = SortSaveAsCSV(people5, 2);
if (csv5 != NULL && strcmp(csv5, "Alice,25,Beijing\nZoe,29,\n") == 0 && malloc_usable_size(csv5) < MAX_MALLOC)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败:空城市未正确处理或内存开辟过大\n");
}
free(csv5);
total_count++;

// 测试用例6:姓名或城市含特殊字符
printf("\n测试用例6:姓名/城市含特殊字符\n");
struct Person people6[] = {
(struct Person){"Li_42", 29, "New-York"},
(struct Person){"OOP_Programmer", 25, "San-Francisco"}
};
char *csv6 = SortSaveAsCSV(people6, 2);
if (csv6 != NULL && strcmp(csv6, "OOP_Programmer,25,San-Francisco\nLi_42,29,New-York\n") == 0 && malloc_usable_size(csv6) < MAX_MALLOC)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败:特殊字符未原样输出或内存开辟过大\n");
}
free(csv6);
total_count++;

// 测试用例7:负年龄
printf("\n测试用例7:负年龄\n");
struct Person people7[] = {
(struct Person){"Zoe", -5, "Hangzhou"},
(struct Person){"Alice", 100, "Beijing"}
};
char *csv7 = SortSaveAsCSV(people7, 2);
if (csv7 != NULL && strcmp(csv7, "Zoe,-5,Hangzhou\nAlice,100,Beijing\n") == 0 && malloc_usable_size(csv7) < MAX_MALLOC)
{
printf(" ✔️ 通过\n");
pass_count++;
}
else
{
printf(" ❌ 失败:负数年龄未正确输出或内存开辟过大\n");
}
free(csv7);
total_count++;

// 汇总结果
printf("\n=== 测试结果 ===\n");
printf("通过: %d/%d (%.1f%%)\n", pass_count, total_count, (float)pass_count / total_count * 100);
}

//未写待补