本课学习目标
- 理解内存资料与文件资料的区别。
- 认识 System.IO 命名空间。
- 使用 File.WriteAllText 写入文本文件。
- 使用 File.ReadAllText 读取整个文本文件。
- 使用 File.AppendAllText 追加内容。
- 使用 File.Exists 检查文件是否存在。
- 使用 Path.Combine 安全组合文件路径。
- 使用 WriteAllLines 与 ReadAllLines 处理多行资料。
- 把 List(Of Student) 保存到文件,并重新读取为对象。
- 完成学生文件管理器。
17.1 内存与文件有什么不同?
变量、数组与 List(Of T) 通常存在内存中;程序结束后,这些数据不会自动保留下来。文件则保存在磁盘中,可以跨越多次程序运行继续使用。
速度快,适合程序运行期间操作;关闭程序后资料通常消失。
可以长期保存,并在下一次启动程序时重新读取。
17.2 System.IO 命名空间
.NET 的文件与路径功能主要位于 System.IO 命名空间。
在 Form1.vb 最上方加入:
Imports System.IO
这样就可以直接写:
File.WriteAllText(...)
Path.Combine(...)
而不必每次写完整名称:
System.IO.File.WriteAllText(...)
System.IO.Path.Combine(...)
17.3 File.WriteAllText:写入整个文本文件
最简单的文件写入方式:
Dim filePath As String =
Path.Combine(
Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments),
"vb2026-note.txt")
File.WriteAllText(
filePath,
"欢迎学习 Visual Basic 2026。")
执行后,程序会在用户的 Documents 文件夹中建立或覆盖:
vb2026-note.txt
My Documents
+
vb2026-note.txt
→
Path.Combine(...)
17.4 File.ReadAllText:读取整个文件
If File.Exists(filePath) Then
Dim content As String =
File.ReadAllText(filePath)
txtOutput.Text = content
Else
txtOutput.Text = "文件不存在。"
End If
ReadAllText 会把整个文本文件读成一个 String。
17.5 File.Exists:先检查文件
读取文件之前最好先确认它存在:
If File.Exists(filePath) Then
txtOutput.Text = File.ReadAllText(filePath)
Else
MessageBox.Show("找不到文件。")
End If
这样可以避免程序直接尝试读取不存在的文件。
17.6 File.AppendAllText:在文件末尾加入内容
File.AppendAllText(
filePath,
"第二行内容" & Environment.NewLine)
与 WriteAllText 不同,AppendAllText 不会先清除旧内容,而是把新内容加到文件末尾。
重新写入
文件存在时覆盖原内容。
追加内容
保留旧内容,在末尾继续加入。
整个读取
一次把文件内容读成 String。
17.7 为什么使用 Path.Combine?
不要手工拼接:
folderPath & "\" & fileName
更推荐:
Dim filePath As String =
Path.Combine(folderPath, fileName)
Path.Combine 会帮你正确处理路径分隔符,代码也更容易阅读。
17.8 使用 Documents 文件夹
可以通过 .NET 取得用户 Documents 文件夹:
Dim documentsFolder As String =
Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments)
再组合完整路径:
Dim filePath As String =
Path.Combine(
documentsFolder,
"students.txt")
17.9 WriteAllLines 与 ReadAllLines
如果资料天然是一行一条记录,可以使用:
Dim lines() As String = {
"李明|Visual Basic 2026|88",
"王芳|C# Programming|76",
"陈伟|Python Programming|91"
}
File.WriteAllLines(filePath, lines)
读取:
Dim lines() As String =
File.ReadAllLines(filePath)
然后逐行处理:
For Each line As String In lines
txtOutput.Text &=
line & Environment.NewLine
Next
17.10 简单记录格式
为了让本课专注于文件读写,我们暂时使用竖线 | 分隔每个字段:
李明|Visual Basic 2026|88
它代表:
姓名|课程|成绩
读取后使用 Lesson 11 的 Split:
Dim parts() As String =
line.Split("|"c)
然后:
Dim studentName As String = parts(0)
Dim course As String = parts(1)
Dim scoreText As String = parts(2)
17.11 把 List(Of Student) 写入文件
假设 Form1 已有:
Private ReadOnly students As New List(Of Student)()
可以先把每个 Student 转成一行文字:
Dim lines As New List(Of String)()
For Each student As Student In students
Dim line As String =
$"{student.Name}|{student.Course}|{student.Score}"
lines.Add(line)
Next
File.WriteAllLines(
studentFilePath,
lines)
17.12 从文件重新建立 Student 对象
students.Clear()
Dim lines() As String =
File.ReadAllLines(studentFilePath)
For Each line As String In lines
Dim parts() As String =
line.Split("|"c)
If parts.Length = 3 Then
Dim score As Double
If Double.TryParse(parts(2), score) Then
Dim student As New Student(
parts(0),
parts(1),
score)
students.Add(student)
End If
End If
Next
这里把之前学过的 String、数组、TryParse、Student 类和 List(Of Student) 全部组合起来。
17.13 实作:学生文件管理器
建立新的 Windows Forms 项目:
StudentFileManager2026
项目保留 Student.vb,Form1 负责新增学生、显示列表、保存到文件和从文件读取。
Form1 建议加入:
| 控件 | Name | Text / 设置 |
|---|---|---|
| TextBox | txtStudentName | 输入学生姓名 |
| TextBox | txtCourse | 输入课程名称 |
| TextBox | txtScore | 输入 0–100 |
| Button | btnAddStudent | Text = 加入学生 |
| Button | btnShowAll | Text = 显示列表 |
| Button | btnSaveFile | Text = 保存文件 |
| Button | btnLoadFile | Text = 读取文件 |
| TextBox | txtOutput | Multiline = True;ReadOnly = True;ScrollBars = Vertical |
Form1.vb 顶部
Imports System.IO
Public Class Form1
Private ReadOnly students As New List(Of Student)()
Private ReadOnly studentFilePath As String =
Path.Combine(
Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments),
"students2026.txt")
加入学生按钮
Private Sub btnAddStudent_Click(
sender As Object,
e As EventArgs) Handles btnAddStudent.Click
Dim studentName As String =
txtStudentName.Text.Trim()
Dim course As String =
txtCourse.Text.Trim()
Dim score As Double
If String.IsNullOrWhiteSpace(studentName) Then
MessageBox.Show("请输入学生姓名。")
txtStudentName.Focus()
Exit Sub
End If
If String.IsNullOrWhiteSpace(course) Then
MessageBox.Show("请输入课程名称。")
txtCourse.Focus()
Exit Sub
End If
If Not Double.TryParse(txtScore.Text, score) Then
MessageBox.Show("请输入有效成绩。")
txtScore.Focus()
Exit Sub
End If
If score < 0 OrElse score > 100 Then
MessageBox.Show("成绩必须介于 0 与 100。")
txtScore.Focus()
Exit Sub
End If
students.Add(
New Student(
studentName,
course,
score))
txtStudentName.Clear()
txtCourse.Clear()
txtScore.Clear()
txtStudentName.Focus()
DisplayStudents()
End Sub
显示学生列表
Private Sub DisplayStudents()
txtOutput.Clear()
txtOutput.Text =
$"文件:{studentFilePath}" &
Environment.NewLine &
$"学生人数:{students.Count}" &
Environment.NewLine &
Environment.NewLine
For Each student As Student In students
txtOutput.Text &=
$"{student.Name} | " &
$"{student.Course} | " &
$"{student.Score:N0}" &
Environment.NewLine
Next
End Sub
显示列表按钮
Private Sub btnShowAll_Click(
sender As Object,
e As EventArgs) Handles btnShowAll.Click
DisplayStudents()
End Sub
保存文件按钮
Private Sub btnSaveFile_Click(
sender As Object,
e As EventArgs) Handles btnSaveFile.Click
Dim lines As New List(Of String)()
For Each student As Student In students
lines.Add(
$"{student.Name}|" &
$"{student.Course}|" &
$"{student.Score}")
Next
File.WriteAllLines(
studentFilePath,
lines)
MessageBox.Show(
"学生资料已经保存。" &
Environment.NewLine &
studentFilePath)
End Sub
读取文件按钮
Private Sub btnLoadFile_Click(
sender As Object,
e As EventArgs) Handles btnLoadFile.Click
If Not File.Exists(studentFilePath) Then
MessageBox.Show("找不到学生资料文件。")
Exit Sub
End If
students.Clear()
Dim lines() As String =
File.ReadAllLines(studentFilePath)
For Each line As String In lines
Dim parts() As String =
line.Split("|"c)
If parts.Length = 3 Then
Dim score As Double
If Double.TryParse(
parts(2),
score) Then
students.Add(
New Student(
parts(0),
parts(1),
score))
End If
End If
Next
DisplayStudents()
MessageBox.Show(
$"已读取 {students.Count} 位学生。")
End Sub
End Class
17.14 程序执行流程
用户输入通过验证后,建立 Student 对象并加入 students。
每个 Student 变成 Name|Course|Score 一行记录。
多条记录一次保存到 students2026.txt。
每一行重新拆成姓名、课程和成绩。
从文本资料恢复为对象,再加入 List(Of Student)。
17.15 怎样测试文件读写?
- 加入两位学生。
- 点击“保存文件”。
- 到 Documents 文件夹确认 students2026.txt 已建立。
- 用记事本打开,确认每位学生占一行。
- 关闭并重新运行程序。
- 点击“读取文件”。
- 确认学生资料重新出现在列表输出中。
17.16 初学者常见错误
读取前不检查 File.Exists
第一次运行时文件可能尚未建立,应先检查。
误用 WriteAllText 覆盖旧内容
需要追加内容时应该使用 AppendAllText,或者先准备完整内容再一次写入。
Split 后直接假设有三个字段
应先检查 parts.Length,避免损坏或不完整记录造成问题。
保存对象本身的想法不清楚
文本文件保存的是文字表示;读取时需要把文字重新解析成对象。
17.17 小练习
- 新增“追加日志”按钮,把当前日期时间写入 activity.log,使用 AppendAllText。
- 加入“文件是否存在”按钮,使用 File.Exists。
- 为 Student 增加 StudentId,并把它加入保存格式。
- 读取文件时,遇到无效成绩记录就跳过,并统计跳过多少行。
- 新增“清除文件”按钮,使用 File.WriteAllText(studentFilePath, "")。
- 尝试把课程名称包含空格的学生保存并重新读取,确认不会受影响。
本课复习
- 为什么 List(Of Student) 不能自动在程序关闭后保留资料?
- Imports System.IO 的作用是什么?
- WriteAllText 和 AppendAllText 有什么区别?
- 为什么读取文件前应该调用 File.Exists?
- Path.Combine 为什么比手工拼接路径更好?
- WriteAllLines 适合什么数据结构?
- 读取 Student 记录时,为什么需要 Split?
- 为什么 Split 后要检查 parts.Length?
- 文本文件中的一行怎样重新变成 Student 对象?
- 怎样确认“保存资料”真的具备持久化效果?