文件与结构体
对应教学日历:讲次 23(6月18日)
一、文件操作
文件的基本概念
- C 语言将文件视为字节流(文本文件 vs 二进制文件)
- 使用
FILE *类型指针操作文件 - 文件使用前必须打开(
fopen),使用后必须关闭(fclose)
文件打开与关闭
FILE *fp;
fp = fopen("文件名", "模式");
if (fp == NULL) {
printf("文件打开失败!\n");
exit(1);
}
// ... 读写操作 ...
fclose(fp);常用打开模式
| 模式 | 含义 |
|---|---|
"r" | 只读,文件必须存在 |
"w" | 只写,文件存在则清空,不存在则新建 |
"a" | 追加写入,文件不存在则新建 |
"r+" | 读写,文件必须存在 |
"w+" | 读写,文件存在则清空,不存在则新建 |
"a+" | 读写,追加模式 |
"rb" / "wb" | 二进制模式读写 |
文件读写函数
字符读写
ch = fgetc(fp); // 从文件读一个字符,返回 EOF 表示结束
fputc(ch, fp); // 写入一个字符到文件字符串读写
fgets(str, n, fp); // 读一行(最多 n-1 字符),保留 \n
fputs(str, fp); // 写入字符串(不自动加 \n)格式化读写
fscanf(fp, "%d %s", &n, name); // 从文件按格式读取
fprintf(fp, "%d %s\n", n, name); // 按格式写入文件
fscanf和fprintf的用法与scanf/printf相同,只是多了一个FILE *参数。
文件结束判断
while (!feof(fp)) { ... } // feof 判断是否到达文件末尾
while (fscanf(fp, "%s", s) != EOF) { ... } // scanf 系列返回 EOF
while (fgets(str, 100, fp) != NULL) { ... } // fgets 返回 NULL完整文件读写流程
#include <stdio.h>
int main() {
FILE *fin, *fout;
fin = fopen("input.txt", "r");
if (fin == NULL) { printf("无法打开输入文件\n"); return 1; }
fout = fopen("output.txt", "w");
if (fout == NULL) { printf("无法创建输出文件\n"); fclose(fin); return 1; }
char line[256];
while (fgets(line, sizeof(line), fin) != NULL) {
fprintf(fout, "%s", line); // 逐行复制
}
fclose(fin);
fclose(fout);
return 0;
}二、结构体(struct)
为什么需要结构体
结构体将不同类型的数据组合成一个整体。如:学生(姓名+学号+成绩)、股票(名称+价格+收益)。
结构体定义
struct 结构体名 {
类型1 成员1;
类型2 成员2;
...
};
struct Student {
char name[20];
int id;
float score;
};结构体变量
// 方法1:先定义结构体,再定义变量
struct Student s1, s2;
// 方法2:定义结构体的同时定义变量
struct Student {
char name[20];
int id;
float score;
} s1, s2;
// 方法3:定义时初始化
struct Student s = {"Tom", 1001, 95.5};访问结构体成员
s1.id = 1001; // . 运算符
strcpy(s1.name, "Tom");
printf("%s %d %.1f\n", s1.name, s1.id, s1.score);结构体数组
struct Student class[30];
for (i = 0; i < 30; i++) {
scanf("%s %d %f", class[i].name, &class[i].id, &class[i].score);
}结构体指针
struct Student s, *p = &s;
p->id = 1001; // 等价于 (*p).id = 1001
p->score = 90.5; // -> 运算符:指针访问成员
// . 和 -> 的区别
s.name // 变量用 .
p->name // 指针用 ->
(*p).name // 解引用后用 .结构体作函数参数
void printStudent(struct Student s) // 传值(复制整个结构体)
{
printf("%s %d\n", s.name, s.id);
}
void modifyStudent(struct Student *p) // 传地址(高效)
{
p->score += 5; // 修改实参
}三、typedef —— 类型别名
typedef 原类型 新别名;
typedef int Integer; // Integer 就是 int
typedef struct Student Student; // 简化:Student 代替 struct Student
typedef struct {
char name[20];
double price;
} Stock; // 定义结构体的同时 typedef// 使用 typedef 后可以省略 struct 关键字
Stock s1, s2; // 而不是 struct Stock s1;
Portfolio best; // 而不是 struct Portfolio best;作业示例
ex14-5:文件读写——身份证提取生日
#include <stdio.h>
#include <string.h>
int main() {
FILE *fin, *fout;
char name[50];
char idcard[20];
char birthday[9];
fin = fopen("idcard.txt", "r");
if (fin == NULL) {
printf("无法打开文件 idcard.txt\n");
return 1;
}
fout = fopen("birthday.txt", "w");
if (fout == NULL) {
printf("无法创建文件 birthday.txt\n");
fclose(fin);
return 1;
}
while (fscanf(fin, "%s", name) == 1) {
if (fscanf(fin, "%s", idcard) != 1) break;
strncpy(birthday, idcard + 6, 8); // 身份证第7-14位是出生日期
birthday[8] = '\0';
fprintf(fout, "%s\n", name);
fprintf(fout, "%s\n", birthday);
}
fclose(fin);
fclose(fout);
printf("生日信息已成功写入 birthday.txt\n");
return 0;
}要点:
fopen("r")读模式,fopen("w")写模式- 必须先检查文件是否打开成功(
fin == NULL) fscanf(fin, "%s", name)从文件读取,返回成功匹配的项数strncpy(dst, src, n)截取 n 个字符idcard + 6指针偏移,指向第 7 个字符(身份证生日起始位置)- 用完后必须
fclose关闭文件
exprp/ex3.c:综合项目——投资组合优化系统(结构体 + 文件 + 函数)
这是本课程的大作业,综合运用了结构体、文件操作、函数模块化、蒙特卡洛模拟等知识。这里列出关键片段:
// 结构体定义
typedef struct {
char name[20];
double prices[MAX_DAYS];
double returns[MAX_DAYS];
int num_days;
double mean_return;
double annual_return;
double volatility;
} Stock;
typedef struct {
double weights[MAX_STOCKS];
double portfolio_return;
double portfolio_risk;
double sharpe_ratio;
} Portfolio;
// 文件读取
int load_from_file() {
FILE *fp = fopen("stock_data.txt", "r");
if (!fp) { printf("错误:无法打开文件\n"); return 0; }
fscanf(fp, "%d %d", &num_stocks, &days);
for (int i = 0; i < num_stocks; i++) {
fscanf(fp, "%19s", stocks[i].name);
for (int j = 0; j < days; j++)
fscanf(fp, "%lf", &stocks[i].prices[j]);
calculate_returns(&stocks[i]); // 函数调用
}
fclose(fp);
return 1;
}
// 写入结果报告
void save_results(Portfolio *best, Portfolio *min_risk) {
FILE *fp = fopen("portfolio_result.txt", "w");
fprintf(fp, "=== 投资组合优化分析报告 ===\n");
fprintf(fp, "夏普比率: %.4f\n", best->sharpe_ratio);
// ...
fclose(fp);
}本项目综合要点:
- 结构体:定义
Stock和Portfolio类型,组织多维数据 - typedef:简化类型名,
Stock代替struct { ... } - 文件操作:
fopen/fclose、fscanf/fprintf、错误处理 - 函数模块化:
load_from_file、calculate_returns、monte_carlo_simulation、save_results等 - 菜单驱动:
switch(choice)结构实现交互菜单 - 结构体指针:
calculate_returns(&stocks[i])传结构体地址 - 动态内存与算法:蒙特卡洛模拟、协方差矩阵计算
作业重点
文件操作
fopen("文件名", "模式")打开文件,模式"r"/"w"/"a"- 打开后必须检查
NULL,用完后必须fclose fscanf/fprintf比scanf/printf多一个FILE *参数fgets读一行(更安全),会保留换行符feof(fp)判断文件末尾
结构体
struct 类型名 { 成员; };定义新类型- 访问成员:变量用
.(如s.name),指针用->(如p->name) - 结构体可嵌套定义(一个结构体的成员可以是另一个结构体)
- 结构体数组:
struct Student class[30]; typedef简化类型名,避免重复写struct