You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#使用Open XML读取Excel时字符串值显示为整数的问题求助

问题描述

在.NET应用中使用Open XML读取Excel文件,编写了根据行和列参数返回对应单元格值的方法,但遇到单元格字符串数据被读取为整数,整数值读取正常的问题。

实现代码

GetServicePorts方法

public List<ServicePortDto> GetServicePorts(IFormFile formFile, Dictionary<string, int> starRowForPorts)
{
    using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(formFile.OpenReadStream(), false))
    {
        WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
        List<int> portRows = new();
        int counter = 0;
        IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
        foreach (var sheet in sheets)
        {
            int startRowPort = starRowForPorts.Where(x=>x.Key == sheet.Name.Value).Select(x => x.Value).FirstOrDefault();
            string relationshipId = sheet.Id.Value;
            WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
            Worksheet workSheet = worksheetPart.Worksheet;
            SheetData sheetData = workSheet.GetFirstChild<SheetData>();
            IEnumerable<Row> rows = sheetData.Descendants<Row>();
            rows = rows.Where(x=>x.RowIndex >= startRowPort);
            foreach (Row row in rows)
            {
                if(row.RowIndex >= startRowPort)
                {
                    string portName = GetCell(workSheet, "B", startRowPort).InnerText;

                    if (portName != null)
                    {
                        counter = counter + 1;
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
    }
    return new List<ServicePortDto>();
}

GetCell方法

private static Cell GetCell(Worksheet worksheet, string columnName, int rowIndex)
{
    Row row = worksheet.GetFirstChild<SheetData>().Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex);

    if (row != null)
    {
        return row.Elements<Cell>().FirstOrDefault(c => string.Compare(c.CellReference.Value, columnName + rowIndex, true) == 0);
    }
    return null;
}

问题详情

读取单元格时,字符串内容返回的是整数(比如原本是"端口A",结果读出来是"1"),但整数类型的单元格内容读取正常。


解决方案

问题根源是Open XML的存储机制:Excel会把重复字符串存入共享字符串表(SharedStringTable),单元格的InnerText存储的是该字符串在表中的索引值,而非实际内容;整数类型则直接存储值,所以读取正常。

需要新增方法解析单元格实际值,步骤如下:

  1. 判断单元格数据类型,若为共享字符串类型,去共享字符串表查找对应索引的实际字符串;
  2. 其他类型直接返回InnerText。

新增辅助方法

// 获取共享字符串表
private static SharedStringTablePart GetSharedStringTablePart(SpreadsheetDocument document)
{
    return document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
}

// 解析单元格的实际值
private static string GetCellValue(Cell cell, SharedStringTablePart sharedStringTablePart)
{
    if (cell == null) return null;

    string cellValue = cell.InnerText;

    // 判断是否为共享字符串类型
    if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
    {
        if (sharedStringTablePart != null && int.TryParse(cellValue, out int index))
        {
            // 根据索引从共享字符串表中获取实际字符串
            var sharedStringItem = sharedStringTablePart.SharedStringTable.Elements<SharedStringItem>().ElementAtOrDefault(index);
            return sharedStringItem?.InnerText;
        }
    }

    // 非共享字符串类型直接返回值
    return cellValue;
}

修改GetServicePorts方法

加入共享字符串表的获取,并用GetCellValue替代直接读取InnerText,同时修复原代码中一直读取同一行的问题:

public List<ServicePortDto> GetServicePorts(IFormFile formFile, Dictionary<string, int> starRowForPorts)
{
    var servicePorts = new List<ServicePortDto>();
    using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(formFile.OpenReadStream(), false))
    {
        WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
        // 获取共享字符串表
        var sharedStringTablePart = GetSharedStringTablePart(spreadSheetDocument);
        
        IEnumerable<Sheet> sheets = workbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
        foreach (var sheet in sheets)
        {
            // 简化startRowPort的获取逻辑
            if (!starRowForPorts.TryGetValue(sheet.Name.Value, out int startRowPort))
            {
                continue;
            }
            
            string relationshipId = sheet.Id.Value;
            WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(relationshipId);
            Worksheet workSheet = worksheetPart.Worksheet;
            SheetData sheetData = workSheet.GetFirstChild<SheetData>();
            IEnumerable<Row> rows = sheetData.Descendants<Row>().Where(x => x.RowIndex >= startRowPort);
            
            foreach (Row row in rows)
            {
                // 读取当前循环行的B列单元格,而非固定startRowPort行
                Cell portNameCell = GetCell(workSheet, "B", (int)row.RowIndex);
                string portName = GetCellValue(portNameCell, sharedStringTablePart);

                if (!string.IsNullOrEmpty(portName))
                {
                    // 按需实例化ServicePortDto并添加到列表
                    // servicePorts.Add(new ServicePortDto { PortName = portName });
                }
                else
                {
                    break;
                }
            }
        }
    }
    return servicePorts;
}

额外优化点

  • 原GetCell方法每次遍历所有行找指定行,可缓存行索引与行的映射提升效率;
  • 用TryGetValue替代LINQ查询获取startRowPort,减少不必要的计算;
  • 修复原代码中循环始终读取同一行单元格的错误,改为读取当前循环行的对应单元格。

内容的提问来源于stack exchange,提问作者Niranjan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:13:17