C#无依赖Excel库转XLS到CSV:阻止数字自动转为日期
解决无Excel库依赖下XLS转CSV时数字被误转日期的问题
嘿,我懂你现在卡在哪了——不用任何Excel库把XLS转成CSV,结果碰到个糟心问题:某行的数字因为上方单元格是日期格式,居然被自动转成了日期,还没法动原始文件的格式。这事儿其实是因为你解析XLS的时候,可能直接拿了单元格的格式化显示文本,而没区分单元格的原始数据类型和格式信息,导致程序误判了数字的类型。
核心问题分析
XLS是二进制格式,每个单元格的格式信息是单独存储的。如果你的解析逻辑只是顺着单元格的显示值读取,当上方单元格用了日期格式,后续单元格可能会被解析器默认套用相同格式,把纯数字(比如20240520)当成日期值(转成2024-05-20)输出到CSV里。要解决这个问题,得从「读取原始值+判断格式类型」入手。
修正后的转换代码
下面是调整后的代码逻辑,重点是读取单元格的原始值,只在确认是日期格式时才做日期转换,否则保留数字的原始字符串形式:
using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; // 假设你已经有XLS解析的基础逻辑,这里补充核心处理部分 protected static void convertExcelToCsv(string inputFile, string outputFile) { using (var fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read)) using (var sw = new StreamWriter(outputFile, false, Encoding.UTF8)) { // 替换成你实际读取工作表行和单元格的逻辑 foreach (var row in GetWorksheetRows(fs)) { var csvCellList = new List<string>(); foreach (var cell in row.Cells) { string cellOutput; // 关键:读取单元格原始值,而非格式化后的显示文本 if (cell.DataType == CellDataType.Numeric) { var rawNumericValue = cell.RawValue; // 检查单元格的格式代码是否为日期格式 var formatCode = cell.FormatCode; if (!string.IsNullOrWhiteSpace(formatCode) && IsDateFormat(formatCode)) { // Excel日期是从1900-01-01开始的天数,注意处理闰年bug var excelDate = Convert.ToDouble(rawNumericValue, CultureInfo.InvariantCulture); var actualDate = DateTime.FromOADate(excelDate); cellOutput = actualDate.ToString("yyyy-MM-dd"); // 按需调整日期格式 } else { // 非日期格式的数字,直接转成不变文化的字符串,避免区域差异 cellOutput = Convert.ToString(rawNumericValue, CultureInfo.InvariantCulture); } } else { // 文本、布尔等其他类型直接处理 cellOutput = cell.RawValue?.ToString() ?? string.Empty; } // 处理CSV的转义规则(包含逗号、引号、换行的情况) csvCellList.Add(EscapeForCsv(cellOutput)); } sw.WriteLine(string.Join(",", csvCellList)); } } } // 辅助方法:判断单元格格式代码是否属于日期/时间格式 private static bool IsDateFormat(string formatCode) { // Excel日期格式通常包含d、m、y、h、s等关键字,排除百分比等类似格式 var dateKeywords = new[] { "d", "M", "y", "h", "s", "tt" }; return dateKeywords.Any(keyword => formatCode.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) && !formatCode.Contains("%"); } // 辅助方法:CSV内容转义,符合RFC4180规范 private static string EscapeForCsv(string value) { if (value == null) return string.Empty; if (value.Contains(",") || value.Contains("\"") || value.Contains("\n") || value.Contains("\r")) { return $"\"{value.Replace("\"", "\"\"")}\""; } return value; } // 这里是占位的行读取方法,替换成你实际的XLS解析逻辑 private static IEnumerable<WorksheetRow> GetWorksheetRows(FileStream fs) { // 你的XLS行解析代码,返回包含Cells的行集合 yield break; } // 占位的单元格相关类型,根据你的解析逻辑调整 public enum CellDataType { Numeric, Text, Boolean, Empty } public class WorksheetRow { public List<WorksheetCell> Cells { get; set; } } public class WorksheetCell { public CellDataType DataType { get; set; } public object RawValue { get; set; } public string FormatCode { get; set; } }
关键调整点
- 读取原始值:放弃直接拿显示文本,改用
RawValue获取单元格的原始数据(数字就是纯数字,文本就是纯文本)。 - 格式判断:通过
IsDateFormat方法检查单元格的格式代码,只有确认是日期格式时,才把数字转成日期字符串。 - 文化中立:用
CultureInfo.InvariantCulture处理数字转字符串,避免不同区域设置下出现逗号分隔符等问题,保证CSV的兼容性。 - CSV规范:加入
EscapeForCsv方法处理特殊字符,生成符合标准的CSV文件。
如果你的XLS解析逻辑里还没实现读取格式代码的功能,可以查阅微软公开的XLS二进制格式文档,找到FORMAT记录的解析方式,提取每个单元格对应的格式代码即可。
内容的提问来源于stack exchange,提问作者Darrick
相关产品推荐
相关产品推荐

