C#数据记录器无法创建CSV文件 求代码错误排查
我正在开发一个基于C#(Visual Studio)的大学GUI项目,非编程专业。尝试实现一款数据记录器,需记录用户的所有操作及时间戳并保存为.csv文件,但程序始终无法创建该文件。预期关闭应用后文件会出现在桌面,记录所有操作记录,恳请帮忙检查代码是否合理,指出错误位置。
using ClosedXML.Excel; using DocumentFormat.OpenXml.Drawing.Diagrams; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Windows.Forms; using System.IO.Ports; namespace Proyecto_controlLed_Servo { public partial class Form1 : Form { private List<string> receivedDataList = new List<string>(); private bool _isRunning = true; public Form1() { CheckForIllegalCrossThreadCalls = false; receivedDataList = new List<string>(); SP1.DataReceived += new SerialDataReceivedEventHandler(SP1_DataReceived); SP1.ErrorReceived += new SerialErrorReceivedEventHandler(SP1_ErrorReceived); SP1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler); LogAlert("Aplicación iniciada"); } private void bCon_Click(object sender, EventArgs e) { try { if (!SP1.IsOpen) { SP1.PortName = CBPort.Text; SP1.Open(); LogAlert("Conexión Establecida"); receivedDataList.Add("Conexión al puerto COM establecida."); PBConexion.Value = 100; enableComponents(); } } catch (Exception error) { MessageBox.Show("Error al conectarse al puerto: " + error.Message); } } private void bDis_Click(object sender, EventArgs e) { if (SP1.IsOpen) { try { //Enviar comando para apagar steppers y cerrar puerto COM SP1.WriteLine("l"); SP1.Close(); LogAlert("Gimb-App desconectado"); receivedDataList.Add("Se ha desconectado del puerto COM."); MessageBox.Show("Entrada COM desconectada"); PBConexion.Value = 0; labelEstado.Text = "Estado actual: Desconectado"; } catch (Exception error) { MessageBox.Show("Error al desconectar el puerto: " + error.Message); } } } private void BRef_Click(object sender, EventArgs e) { try { string[] ports = SerialPort.GetPortNames(); CBPort.Items.Clear(); CBPort.Items.AddRange(ports); if (ports.Length > 0) { CBPort.SelectedIndex = 0; } MessageBox.Show("Lista de puertos actualizada"); receivedDataList.Add("Se refrescó la lista de puertos COM."); } catch (Exception error) { MessageBox.Show("Error al refrescar la lista de puertos: " + error.Message); } } //Método para registrar alertas private void LogAlert(string message) { string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); receivedDataList.Add($"{timestamp}:{message}"); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { if (SP1.IsOpen) { try { SP1.WriteLine("l"); SP1.Close(); } catch (Exception error) { MessageBox.Show("Error al cerrar la aplicación: " + error.Message); } } _isRunning = false; SaveDataToCsv(); } private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) { if (!_isRunning) return; try { string data = SP1.ReadLine(); receivedDataList.Add("Dato recibido: " + data); Console.WriteLine("Dato recibido: " + data); } catch (Exception error) { receivedDataList.Add("Error al recibir datos: " + error.Message); } } private void SaveDataToCsv() { try { string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); string filePath = Path.Combine(desktopPath, "RegistroGimb.csv"); string csvFilePath = Path.ChangeExtension(filePath, ".csv"); using (var workbook = new XLWorkbook()) { var worksheet = workbook.Worksheets.Add("Registros"); worksheet.Cell(1, 1).Value = "Registro de Actividades"; for (int i = 0; i < receivedDataList.Count; i++) { worksheet.Cell(i + 2, 1).Value = receivedDataList[i]; } workbook.SaveAs("RegistroGimb.xlsx"); //guardar como archivo .xlsx } ConvertXlsxToCsv(filePath, csvFilePath); MessageBox.Show("Registro de alertas guardado en {filePath}"); } catch (Exception error) { MessageBox.Show("Error al escribir en el archivo de registro: " + error.Message); } } private void ConvertXlsxToCsv(string filePath, string csvFilePath) { try { using (var workbook = new XLWorkbook(filePath)) { var worksheet = workbook.Worksheet(1); using (var writer = new StreamWriter(csvFilePath)) { foreach (var row in worksheet.RowsUsed()) { var cells = row.CellsUsed().Select(c => c.GetValue<string>()); var line = string.Join(",", cells); writer.WriteLine(line); } } } } catch (Exception error) { MessageBox.Show("Error al convertir el archivo a CSV: " + error.Message); } } //Método para lectura de puerto serie private void SP1_DataReceived(object sender, SerialDataReceivedEventArgs e) { try { string data = SP1.ReadLine(); if (data.Contains("Movimiento detectado")) { LogAlert("Movimiento detectado"); } else if (data.Contains("Motor endendido")) { LogAlert("Motor encendido"); } else if (data.Contains("Motor apagado")) { LogAlert("Motor apagado"); } else if (data.Contains("Conexión establecida")) { LogAlert("Conexión establecida"); } else if (data.Contains("Desconexion")) { LogAlert("Desconexión del puerto COM"); } else { LogAlert("Dato recibido: " + data); } } catch (Exception error) { MessageBox.Show("Error al leer datos del puerto serie: " + error.Message); LogAlert("Error al leer datos del puerto serie: " + error.Message); } } } }
1. XLSX文件保存路径错误
原代码中workbook.SaveAs("RegistroGimb.xlsx");未指定桌面路径,会将XLSX文件保存到程序运行目录(如bin/Debug),而非桌面。后续ConvertXlsxToCsv尝试从桌面路径读取该文件,必然找不到,导致转换失败。
修正:保存XLSX时指定桌面路径:
string xlsxPath = Path.Combine(desktopPath, "RegistroGimb.xlsx"); workbook.SaveAs(xlsxPath);
2. 转换CSV时参数传递错误
调用ConvertXlsxToCsv(filePath, csvFilePath);时,filePath是桌面的CSV路径,但实际要读取的是刚保存的XLSX文件,路径不匹配。
修正:传入正确的XLSX路径:
ConvertXlsxToCsv(xlsxPath, csvFilePath);
3. 字符串插值未生效
MessageBox.Show("Registro de alertas guardado en {filePath}");未使用$符号做字符串插值,会直接显示{filePath}而非实际路径。
修正:
MessageBox.Show($"Registro de alertas guardado en {csvFilePath}");
4. 串口数据重复处理
构造函数中同时绑定了SP1_DataReceived和DataReceivedHandler两个SerialDataReceived事件,会导致串口数据被两次读取、日志重复记录。
修正:移除其中一个事件绑定,保留SP1_DataReceived即可(该方法已包含日志记录逻辑):
// 移除这一行 // SP1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
5. 冗余的XLSX转CSV流程
直接将receivedDataList写入CSV即可,无需先存XLSX再转换,既浪费资源又增加出错概率。
简化后的SaveDataToCsv方法:
private void SaveDataToCsv() { try { string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); string csvFilePath = Path.Combine(desktopPath, "RegistroGimb.csv"); // 直接写入CSV,跳过XLSX步骤 using (var writer = new StreamWriter(csvFilePath)) { writer.WriteLine("Registro de Actividades"); foreach (var item in receivedDataList) { // 如果内容包含逗号,用引号包裹避免CSV格式错误 var formattedLine = item.Contains(",") ? $"\"{item}\"" : item; writer.WriteLine(formattedLine); } } MessageBox.Show($"Registro de alertas guardado en {csvFilePath}"); } catch (Exception error) { MessageBox.Show("Error al escribir en el archivo de registro: " + error.Message); } }
同时可以删除ConvertXlsxToCsv方法和ClosedXML.Excel的引用,减少依赖。
内容的提问来源于stack exchange,提问作者Marshall

