一、CSV文件基础认知(必背知识点)

1. 什么是CSV文件

CSV全称逗号分隔值文件,是一种纯文本轻量化表格文件,无需依赖Excel软件即可打开、编辑、读写,常用于存储小型结构化数据、日志数据、简单数据表。

2. CSV核心格式规则

① 整体结构:表头行 + 数据行

② 列与列之间使用英文逗号 , 分隔

③ 每一行代表一条独立的数据信息,第一行为表头,后续为内容数据

④ 纯文本存储,体积小、读写速度快、兼容性极强

3. 必备引用命名空间

所有文件、文件夹、文本流操作必须引入以下命名空间,否则代码报错:

using System.IO;    //文件、文件夹、文件流核心操作
using System.Text;  //字符串拼接(StringBuilder)
using System.Windows.Forms;

二、项目结构说明

本案例分为两大核心模块:

1、CSVAPI工具类:封装CSV文件创建、表头写入、数据追加、文件读取核心方法,解耦业务代码

2、Form1窗体类:调用工具类,实现一键写入数据、一键解析读取数据功能


第一部分:CSVAPI 工具类完整代码详解

1、工具类命名空间与类定义

namespace _3csv数据格式
{
    //CSV文件读写工具类,封装所有CSV操作方法
    public class CSVAPI
    {

2、写入数据方法 SaveData(核心重点)

功能作用:自动检测/创建存储文件夹、自动创建CSV文件、首次写入表头、后续追加数据,不覆盖原有数据

 /// <summary>
        /// 向CSV文件写入人员数据(自动创建目录、自动生成表头、追加写入)
        /// </summary>
        /// <param name="name">姓名</param>
        /// <param name="age">年龄</param>
        /// <param name="sex">性别</param>
        public static void SaveData(string name, string age, string sex)
        {
            //=====================第一步:处理文件夹路径=====================
            //获取程序运行根目录(Debug文件夹路径)
            string dirPath = Directory.GetCurrentDirectory() + @"\data";

            //判断data文件夹是否存在,不存在则手动创建
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath); 
            }

代码细节解析

Directory.GetCurrentDirectory():获取程序运行的Debug目录,动态适配路径,无需写死绝对路径

Directory.Exists():判断文件夹是否存在,避免重复创建

Directory.CreateDirectory():不存在则自动创建data文件夹,防止路径不存在报错

            //=====================第二步:处理文件路径=====================
            string filePath = dirPath + @"\1.csv";

            //判断CSV文件是否存在,不存在则创建文件并写入表头
            if (!File.Exists(filePath))
            {
                //创建文件流:指定文件路径、创建模式、写入权限
                FileStream fs = new FileStream(filePath,FileMode.Create,FileAccess.Write);
                      
                //创建文本写入流,指定默认编码,解决中文乱码问题
                StreamWriter sw = new StreamWriter(fs,Encoding.Default); 

                //拼接CSV表头:姓名、年龄、性别
                StringBuilder sb  = new StringBuilder();
                sb.Append("姓名").Append(",").Append("年龄").Append(",").Append("性别");

                //将表头写入文件,自动换行
                sw.WriteLine(sb);

                //手动关闭流、释放系统资源(旧版本写法)
                sw.Close();
                fs.Close();
                sw.Dispose();
                fs.Dispose();
            }

核心细节解析

FileMode.Create:文件不存在则创建,存在则覆盖(仅首次创建表头使用)

Encoding.Default:适配系统中文编码,彻底解决CSV中文乱码

StringBuilder:高效拼接字符串,优于字符串直接相加,性能更高

必须手动关闭释放流资源,避免文件被程序占用、无法二次编辑

            //=====================第三步:追加写入数据内容=====================
            //using语法:自动释放流资源,无需手动Close、Dispose
            //第二个参数true:开启【追加模式】,保留旧数据,新增数据换行写入
            using (StreamWriter sw = new StreamWriter(filePath,true,Encoding.Default))
            {
                //拼接当前传入的人员数据
                StringBuilder sb = new StringBuilder();
                sb.Append(name).Append(",").Append(age).Append(",").Append(sex);

                //写入一行数据
                sw.WriteLine(sb);
            }
        }

关键必考知识点

StreamWriter(filePath,true):true=追加模式,保留原有数据

StreamWriter(filePath, false):false=覆盖模式,清空旧数据重写

using语句包裹流对象:代码执行完毕后自动释放资源,杜绝内存泄漏,是开发标准写法

3、读取数据方法 ReadData

功能作用:读取完整CSV文件所有文本内容,以字符串形式返回

        /// <summary>
        /// 读取CSV文件全部文本数据
        /// </summary>
        /// <returns>CSV完整文本字符串</returns>
        public static string ReadData()
        {
            //1、拼接文件夹路径,自动补全目录
            string dirPath = Directory.GetCurrentDirectory() + @"\data";
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }

            //2、拼接完整文件路径
            string filePath = dirPath + @"\1.csv";

            //3、创建读取流,读取文件内容
            StreamReader sr = new StreamReader(filePath,Encoding.Default); 
            
            //读取文件所有内容,一次性全部读取
            string ss  =  sr.ReadToEnd(); 

            //4、释放资源
            sr.Close();
            sr.Dispose();

            //返回完整CSV文本
            return ss;
        }
    }
}

核心方法解析

ReadToEnd():读取文件全部文本,包含所有表头、数据、换行符

读取后的字符串保留原始CSV格式:换行\n、逗号, 分隔符,为后续分割解析做铺垫


第二部分:窗体调用代码完整详解

1、窗体初始化代码

namespace _3csv数据格式
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

2、按钮1:写入CSV测试数据

功能:点击按钮,自动向CSV写入一条模拟人员数据

        private void button1_Click(object sender, EventArgs e)
        {
            //调用工具类静态方法,写入固定模拟数据
            CSVAPI.SaveData("梅西","20","男");
        }

3、按钮2:读取并解析CSV数据(核心难点)

功能:读取完整CSV文本,通过分割算法,剔除表头、空行,解析出每一个单元格数据

        private void button2_Click(object sender, EventArgs e)
        {
            //1、调用工具类读取全部CSV文本
            string data =  CSVAPI.ReadData();

            //2、第一次分割:按【换行符\n】分割,将文本拆分为【每一行数据】
            string[] arr = data.Split('\n');

            //3、创建新数组,剔除表头和最后空行
            //arr.Length-2:减去第一行表头、最后一行空数据
            string[] newArr = new string[arr.Length-2];

            //4、循环筛选有效数据行
            for (int i = 0; i < arr.Length; i++)
            {
                //i>0:跳过第0行(表头)
                //i<arr.Length-1:跳过最后一行空行
                if (i>0&&i<arr.Length-1)
                {
                    newArr[i - 1] = arr[i];
                }
            }

筛选逻辑详解

CSV原始读取内容包含:表头行、数据行、末尾空行,必须过滤无效行,否则解析报错

通过下标判断,只保留中间真实的业务数据行

            //5、遍历所有有效数据行
            foreach (string item in newArr)
            {
                //6、第二次分割:按【逗号,】分割,将一行数据拆分为单个单元格数据
                string[] arr1 =  item.Split(',');

                //7、遍历每个单元格数据,弹窗展示
                foreach (var item1 in arr1)
                {
                    MessageBox.Show(item1);
                }
            }
        }
    }
}

第三部分:CSV解析完整执行流程

步骤1:写入流程

判断文件夹 → 无则创建 → 判断文件 → 无则创建并写入表头 → 追加写入数据

步骤2:读取解析流程

读取完整文本 → 按换行分行 → 过滤表头、空行 → 按逗号分列 → 遍历展示单个数据


第四部分:核心知识点汇总

1、文件夹操作核心类 Directory

Directory:文件夹操作(判断、创建目录)

GetCurrentDirectory():获取程序运行根目录

Exists():判断文件夹是否存在

CreateDirectory():创建文件夹

2、文件操作核心类 File、文件流

File.Exists():判断文件是否存在

FileStream:底层文件操作流,负责创建、打开文件

StreamWriter:文本写入,true=追加模式,false=覆盖模式

StreamReader:文本读取,ReadToEnd读取全部内容

StringBuilder:高效拼接字符串,适合批量拼接数据

3、追加与覆盖区别

new StreamWriter(path, true):追加写入,保留旧数据

new StreamWriter(path, false):覆盖写入,清空旧数据

4、CSV双层分割算法(必考)

第一层 Split('\n'):按行分割

第二层 Split(','):按列分割


第五部分:易错点总结

1、未判断文件夹是否存在,直接写入会报路径不存在异常

2、未写入表头,CSV文件无字段,表格结构错乱

3、未开启追加模式,每次写入都会覆盖历史数据

4、未指定编码Encoding.Default,中文乱码

5、解析数据未过滤表头和空行,读取到无效空数据

6、不释放文件流资源,导致文件被占用,无法修改、删除

Settings 用户配置文件读写

一、Settings 配置核心知识点

1. 什么是Settings配置

Settings 是 .NET 框架原生自带的用户配置类,专门用于快速保存软件用户参数、临时配置、个人偏好设置。

对比 INI、CSV:无需手动创建文件、无需读写流、无需DLL调用,一行代码即可读写保存

2. Settings 两种作用域

1、Application 应用程序级:全局只读、所有用户共用、无法代码修改保存

2、User 用户级(本案例使用):可读写、每个用户独立配置、支持代码动态Save保存

3. 存储位置

自动保存到系统用户目录,无需手动管理文件路径,由 .NET 框架自动托管。

4. 支持的数据类型

字符串、整型、布尔、浮点、颜色、字体、路径等常用所有数据类型


二、前期创建步骤(考试/作业必写步骤)

1、右键项目 → 属性 → 设置

2、点击“创建设置文件”

3、添加三个配置项:

名称

类型

范围

默认值

name

string

User

张三

age

int

User

20

sex

bool

User

False

4、保存后自动生成配置节点与Settings类,无需手动手写实体


三、自动生成 App.config 配置文件详解

该文件由VS自动生成,负责记录所有用户配置的默认值、配置节点类型

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- 配置节点声明:固定模板,声明用户配置组 -->
    <configSections>
        <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="_4setting读写.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
        </sectionGroup>
    </configSections>

    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
    </startup>

    <!-- 用户配置区域:所有可读写配置都写在这里 -->
    <userSettings>
        <_4setting读写.Properties.Settings>
            <setting name="name" serializeAs="String">
                <value>张三</value>
            </setting>
            <setting name="age" serializeAs="String">
                <value>20</value>
            </setting>
            <setting name="sex" serializeAs="String">
                <value>False</value>
            </setting>
        </_4setting读写.Properties.Settings>
    </userSettings>
</configuration>

字段解析:

name:配置字段名,对应代码属性

serializeAs="String":以字符串形式序列化存储

value:配置默认初始值


四、窗体业务代码逐段精细拆解

1、命名空间与窗体初始化

namespace _4setting读写
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

2、写入/修改配置按钮(Button1)

核心功能:获取全局配置对象 → 重新赋值 → 持久化保存到本地

        /// <summary>
        /// 写入/修改用户配置
        /// </summary>
        private void button1_Click(object sender, EventArgs e)
        {
            //1、获取系统默认的全局配置单例对象
            //Settings.Default 是固定写法,拿到配置实例
            Settings set = Settings.Default;

            //2、给自定义配置字段重新赋值(覆盖默认值)
            set.name = "李四";
            set.age = 40;
            set.sex = true;

            //3、关键方法:Save() 持久化保存到本地配置文件
            //不调用Save,修改只在内存生效,重启程序失效
            set.Save();
        }

重点必考细节

1、Settings.Default:全局唯一单例对象,固定获取方式

2、赋值后必须调用 Save(),否则数据只存在内存中,重启丢失

3、直接赋值即可自动覆盖原有配置,无需判断、无需新建文件

3、读取配置按钮(Button2)

核心功能:读取本地已保存的配置数据,弹窗展示

 /// <summary>
        /// 读取用户配置
        /// </summary>
        private void button2_Click(object sender, EventArgs e)
        {
            //1、获取配置对象
            Settings set = Settings.Default;

            //2、直接读取配置字段值,弹窗展示
            MessageBox.Show(set.name);
        }
    }
}

读取逻辑:优先读取用户修改保存后的最新值,无修改则读取默认值。


五、完整执行流程

1、手动在项目设置中创建 name、age、sex 三个User类型配置

2、程序自动在App.config生成默认配置数据

3、点击写入按钮:修改内存中配置 → Save保存到本地文件

4、点击读取按钮:加载本地配置 → 获取最新数据展示

5、重启程序数据不丢失,实现永久保存


六、Settings 优缺点总结

优点

1、.NET原生支持,无需引用第三方库、无需Dll导入、无需文件流操作

2、强类型存储(自动识别int、bool、string),不会类型错乱

3、代码极简,只需要 取值、赋值、Save三步

4、自动管理文件,无需关心路径

5、重启程序数据保留,持久化效果好

缺点

1、只适合存储少量配置,不适合大量数据(对比CSV)

2、User级别仅对当前电脑用户生效


七、高频易错点(必背)

1、忘记点击【设置】创建配置文件,直接写代码报错

2、配置范围选错成Application,无法修改、无法Save保存

3、赋值后不调用Save(),数据内存生效、重启丢失

4、字段名大小写不一致,读取不到数据、报错

5、数据类型不匹配(例如int存字符串)编译报错


八、三种本地存储对比总结

INI:依靠系统DLL、手动封装、适合通用配置、跨平台兼容差

CSV:文件流读写、适合大量表格数据、手动解析分割

Settings:原生最强、最简代码、强类型、适合软件小型配置

try-catch 异常捕获与常见异常类型

一、异常基础核心知识点

1. 什么是异常

程序运行中出现的非语法错误,代码语法没问题,但运行时逻辑出错,导致程序直接崩溃退出。

异常不处理:程序闪退、终止运行;

异常捕获处理:程序不崩溃,友好提示错误,可重试继续运行。

2. try-catch 标准结构

try
{
    //有可能出错、会抛异常的风险代码
}
catch (具体异常类型 ex)
{
    //捕获对应异常,处理错误逻辑
}
catch (Exception ex)
{
    //捕获所有未知、剩余所有异常(兜底)
}

3. 异常捕获优先级规则(必考)

1、先写具体异常,最后写Exception基类异常

2、顺序不能颠倒,否则具体异常永远不会执行

3、Exception是所有异常的父类,可以捕获所有异常

4. 代码中常用五大异常(必背)

1. FormatException:数据格式错误(字符串转数字格式非法)

2. OverflowException:数值超出数据类型范围(数字太大/太小)

3. DivideByZeroException:除数为0异常

4. IndexOutOfRangeException:数组/集合索引越界

5. NullReferenceException:空引用异常(对象为null调用属性/下标)

6. Exception:所有异常的基类,万能兜底捕获


二、案例一:数字输入多异常精准捕获(核心案例)

完整解析代码 + 逐行详解

namespace _5try.@catch
{
    internal static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// 
        [STAThread]
        static void Main()
        {
        inputNum: //跳转标签,用于出错重试
            try
            {
                //风险代码:用户输入、类型转换、数学运算都可能报错
                Console.WriteLine("请输入合法数字");
                string s = Console.ReadLine();
                //1、输入字母符号 → 报格式异常
                //2、输入数字超出int范围 → 报溢出异常
                int num = int.Parse(s);
                //计算10的num次方
                double sum = Math.Pow(10, num);
                Console.WriteLine(sum);
            }
            //精准捕获:格式错误异常(输入非数字)
            catch (FormatException ex)
            {
                //ex.Message:获取系统原生异常描述信息
                Console.WriteLine(ex.Message);
                //出错后跳转回输入,重新输入
                goto inputNum;
            }
            //精准捕获:数值超出范围异常
            catch (OverflowException ex)
            {
                Console.WriteLine("输入数字超出范围");
                goto inputNum;
            }
            //兜底捕获:所有未知异常
            catch (Exception ex)
            {
                Console.WriteLine("无法预料的异常");
                //throw ex; //抛出异常,让程序崩溃(不推荐)
            }

逐段逻辑详解

1. goto标签 inputNum

作用:异常出错后,不退出程序,跳转回顶部重新输入,实现错误重试效果

2. FormatException 场景

用户输入:abc、#$%、中文、空内容

int.Parse 无法转换,触发格式异常

ex.Message:打印系统自带的详细错误原因

3. OverflowException 场景

用户输入超大数字,超出 int 类型取值范围

int范围:-2147483648 ~ 2147483647

数字过大/过小,触发数值溢出异常

4. Exception 兜底捕获

捕获前面没有匹配到的所有未知异常,保证程序绝对不崩溃

throw ex; 作用:手动抛出异常,让程序终止(调试用,项目中一般不写)


三、案例二:数组索引越界异常捕获

            //数组索引越界异常案例
        i1:
            //接收用户输入索引
            int a = int.Parse(Console.ReadLine());
            try
            {
                //定义固定长度数组(下标0、1、2、3)
                int[] ss = new int[] { 1, 2, 3, 4, };
                //根据用户输入下标取值,超出0-3直接越界
                Console.WriteLine(ss[a]);
            }
            //精准捕获:索引越界异常
            catch (IndexOutOfRangeException ex)
            {
                Console.WriteLine(ex.Message + "++++++++");
                //出错重试
                goto i1;
            }
            //兜底捕获所有其他异常
            catch (Exception)
            {
                //throw空抛:向上抛出异常,程序崩溃
                throw;
            }

案例解析

数组长度为4,合法下标:0、1、2、3

用户输入 4、5、-1 等非法下标 → 触发 IndexOutOfRangeException

goto i1:越界后重新输入,程序不闪退

throw; 空抛:不传递异常对象,直接向上抛出错误,终止程序


四、案例三:空引用异常演示(NullReferenceException)

            //空引用异常案例
            int[] arr = null;  //数组变量为空,没有指向任何对象
            Console.WriteLine(arr[0]); //空对象调用下标,直接报错

异常原因

变量只是声明,没有实例化、没有开辟内存,值为null

对null对象进行读取、调用、取值,触发空引用异常(项目最常见报错)


五、程序入口窗体启动代码

            //窗体程序固定启动模板
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

六、throw 抛异常核心知识点

1. throw ex;

抛出当前捕获的异常信息,保留完整异常堆栈信息

2. throw;

空抛出,向上传递异常,保留原始异常信息

3. throw new Exception("自定义错误");

手动创建自定义异常,自定义错误提示文本

注意:不写throw,异常被吃掉,程序正常运行,无报错崩溃


七、try-catch 开发规范(必考面试点)

1、能精准捕获就精准捕获,不要全部直接用Exception

2、具体异常在前,Exception兜底在后,顺序绝对不能乱

3、业务异常必须处理,避免程序闪退崩溃

4、可以配合goto实现错误重试机制

5、ex.Message 获取系统标准错误信息,方便排错


八、所有异常场景汇总复盘

FormatException:字符串转数字、格式非法

OverflowException:数值超出类型存储范围

IndexOutOfRangeException:数组、集合下标越界

NullReferenceException:空对象调用方法/属性/下标

DivideByZeroException:除数为0

Exception:所有异常的基类,万能兜底

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐