From 21e5a49b844656d812470647060407a83a77c978 Mon Sep 17 00:00:00 2001 From: MarvelTiter_yaoqinglin Date: Fri, 9 May 2025 18:00:36 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A4=87=E4=BB=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...10\346\234\254\346\227\245\345\277\227.md" | 7 + src/LightExcel/ExcelConfiguration.cs | 2 +- src/LightExcel/ExcelHelper.Template.cs | 24 ++-- src/LightExcel/ExcelReader.cs | 27 ++-- src/LightExcel/LightExcel.csproj | 2 +- .../OpenXml/Basic/NodeCollectionXmlPart.cs | 50 +++---- .../Basic/SimpleNodeCollectionXmlPart.cs | 20 +++ .../OpenXml/Basic/XmlPart.Enumerator.cs | 49 ------- src/LightExcel/OpenXml/Basic/XmlPart.cs | 22 ++- src/LightExcel/OpenXml/Cell.cs | 24 +++- src/LightExcel/OpenXml/ContentTypes.cs | 10 +- .../OpenXml/Interfaces/INodeCollection.cs | 3 +- src/LightExcel/OpenXml/Interfaces/IXmlPart.cs | 6 +- src/LightExcel/OpenXml/LightExcelXmlReader.cs | 131 +++++++++--------- src/LightExcel/OpenXml/MergeCellCollection.cs | 24 ++-- .../OpenXml/RelationshipCollection.cs | 16 +-- src/LightExcel/OpenXml/RowCollection.cs | 38 +++-- src/LightExcel/OpenXml/SharedStringTable.cs | 36 +++-- src/LightExcel/OpenXml/Sheet.cs | 55 ++++---- src/LightExcel/OpenXml/SheetCollection.cs | 9 +- src/LightExcel/OpenXml/SheetCols.cs | 15 +- src/LightExcel/OpenXml/StyleSheet.cs | 7 +- .../OpenXml/Styles/BorderCollection.cs | 18 +-- .../OpenXml/Styles/FontCollection.cs | 10 +- src/LightExcel/Renders/DictionaryRender.cs | 8 +- src/LightExcel/Utils/CellHelper.cs | 2 +- src/LightExcel/Utils/ReferenceHelper.cs | 4 +- src/LightExcel/Utils/XmlHelper.cs | 4 +- test/TestProject1/OpenXmlExcelReadTest.cs | 25 ++-- 29 files changed, 343 insertions(+), 305 deletions(-) create mode 100644 src/LightExcel/OpenXml/Basic/SimpleNodeCollectionXmlPart.cs delete mode 100644 src/LightExcel/OpenXml/Basic/XmlPart.Enumerator.cs diff --git "a/doc/\347\211\210\346\234\254\346\227\245\345\277\227.md" "b/doc/\347\211\210\346\234\254\346\227\245\345\277\227.md" index a38eebd..e6cbdb8 100644 --- "a/doc/\347\211\210\346\234\254\346\227\245\345\277\227.md" +++ "b/doc/\347\211\210\346\234\254\346\227\245\345\277\227.md" @@ -1,5 +1,12 @@ # 版本功能更新记录 +## v2.1.3 + +- 🐞读取行时,校验单元格的位置,确保索引正确 +- ⚡️优化读取时的内存占用 + ## v2.1.2 + - ⚡️调整项目文件结构 - 🐞修复ExcelReader.CellAt方法的索引判断错误 + diff --git a/src/LightExcel/ExcelConfiguration.cs b/src/LightExcel/ExcelConfiguration.cs index cb668af..9c5fcaf 100644 --- a/src/LightExcel/ExcelConfiguration.cs +++ b/src/LightExcel/ExcelConfiguration.cs @@ -35,7 +35,7 @@ public class ExcelConfiguration internal ConcurrentDictionary DynamicColumns { get; set; } = new(); - public ExcelColumnInfo? this[string name] => DynamicColumns.ContainsKey(name) ? DynamicColumns[name] : null; + public ExcelColumnInfo? this[string name] => DynamicColumns.TryGetValue(name, out ExcelColumnInfo? value) ? value : null; public ExcelConfiguration AddDynamicColumnInfo(string name, Action info) { diff --git a/src/LightExcel/ExcelHelper.Template.cs b/src/LightExcel/ExcelHelper.Template.cs index d2c7b64..9522e1b 100644 --- a/src/LightExcel/ExcelHelper.Template.cs +++ b/src/LightExcel/ExcelHelper.Template.cs @@ -22,15 +22,16 @@ public partial class ExcelHelper internal void HandleWriteTemplate(ExcelArchiveEntry doc, object data, string sheetName) { configuration.FillByTemplate = true; - + // 获取sheet对象 var sheet = doc.WorkBook.WorkSheets.FirstOrDefault() ?? throw new Exception("read excel sheet failed"); // 获取最后一行当模板 - var templateRow = sheet.ToList().Last(); + var header = sheet.ToList(); + var templateRow = header.Last(); // 获取共享字符串列表 var sst = doc.WorkBook.SharedStrings?.ToList(); var render = RenderProvider.GetDataRender(data.GetType(), configuration); - var columns = configuration.FillWithPlacholder ? CollectExcelColumnInfos(templateRow, sst).ToArray() : render.CollectExcelColumnInfo(data).ToArray(); + ExcelColumnInfo[]? columns = configuration.FillWithPlacholder ? [.. CollectExcelColumnInfos(templateRow, sst)] : [.. render.CollectExcelColumnInfo(data)]; sheet.Columns = columns; if (configuration.FillWithPlacholder) { @@ -42,14 +43,19 @@ internal void HandleWriteTemplate(ExcelArchiveEntry doc, object data, string she configuration.StartRowIndex = templateRow.RowIndex; } var newRows = render.RenderBody(data, sheet, columns, new TransConfiguration { SheetNumberFormat = configuration.AddSheetNumberFormat }); - sheet.Replace(sheet.Concat(newRows)); + sheet.Replace(header.Concat(newRows)); doc.Save(); } - - static readonly Regex extract = new Regex("{{(.+)}}"); - private IEnumerable CollectExcelColumnInfos(Row templateRow, List? sst) +#if NET8_0_OR_GREATER + [GeneratedRegex("{{(.+)}}")] + private static partial Regex ExtractColumn(); +#else + static readonly Regex extract = new("{{(.+)}}"); + private static Regex ExtractColumn() => extract; +#endif + private static IEnumerable CollectExcelColumnInfos(Row templateRow, List? sst) { - foreach (var cell in templateRow.RowDatas) + foreach (var cell in templateRow.Children) { string? name = cell.Value; var (X, Y) = ReferenceHelper.ConvertCellReferenceToXY(cell.Reference); @@ -62,7 +68,7 @@ private IEnumerable CollectExcelColumnInfos(Row templateRow, Li } if (name != null) { - var match = extract.Match(name); + var match = ExtractColumn().Match(name); if (match.Success) { name = match.Groups[1].Value; diff --git a/src/LightExcel/ExcelReader.cs b/src/LightExcel/ExcelReader.cs index 68d1447..f0d39df 100644 --- a/src/LightExcel/ExcelReader.cs +++ b/src/LightExcel/ExcelReader.cs @@ -21,13 +21,14 @@ internal class ExcelReader : IExcelDataReader public string CurrentSheetName => sheetEnumerator.Current?.Name?.ToString() ?? ""; - public int FieldCount => cells.Length; + public int FieldCount => cells.Count; public int RowIndex => rowEnumerator?.Current.RowIndex ?? 0; - Cell[] cells = []; + List cells = []; string[] heads = []; int startColumn = 1; int startRow = 1; + int fixedColumn = 0; public ExcelReader(ExcelArchiveEntry document, ExcelConfiguration configuration, string? targetSheet = null) { this.document = document; @@ -42,12 +43,12 @@ public ExcelReader(ExcelArchiveEntry document, ExcelConfiguration configuration, private Cell? CellAt(int i) { - System.Diagnostics.Debug.WriteLine($"{RowIndex}: CellAt {i}, Enable {i < 0 || i >= cells.Length}"); - if (i < 0 || i >= cells.Length) + //System.Diagnostics.Debug.WriteLine($"{RowIndex}: CellAt {i}, Enable {i + fixedColumn < 0 || i + fixedColumn >= cells.Count}"); + if (i + fixedColumn < 0 || i + fixedColumn >= cells.Count) return null; - return cells[i]; - } + return cells[i + fixedColumn]; + } public bool GetBoolean(int i) { @@ -137,7 +138,7 @@ public bool NextResult() rowEnumerator = sheetEnumerator.Current.GetEnumerator(); if (configuration.UseHeader && rowEnumerator.MoveNext()) { - heads = [.. rowEnumerator.Current.RowDatas.Select(c => c.GetCellValue(Sst) ?? "")]; + heads = [.. rowEnumerator.Current.Children.Select(c => c.GetCellValue(Sst) ?? "")]; startRow += 1; } return true; @@ -170,11 +171,15 @@ public bool Read() { if (HasRows) { + //if (startColumn > 1) + // cells = rowEnumerator!.Current.RowDatas.Skip(startColumn - 1); + //else if (startColumn > 1) - cells = [.. rowEnumerator!.Current.RowDatas.Skip(startColumn - 1)]; - else - cells = [.. rowEnumerator!.Current.RowDatas]; - System.Diagnostics.Debug.WriteLine($"{RowIndex}: Read, Cells {cells.Length}"); + { + fixedColumn = startColumn - 1; + } + cells = rowEnumerator!.Current!.Children; + //System.Diagnostics.Debug.WriteLine($"{RowIndex}: Read, Cells {cells.Count}"); return true; } return false; diff --git a/src/LightExcel/LightExcel.csproj b/src/LightExcel/LightExcel.csproj index eb782d4..58076c2 100644 --- a/src/LightExcel/LightExcel.csproj +++ b/src/LightExcel/LightExcel.csproj @@ -1,7 +1,7 @@ - net45;net6.0; + net45;net6.0;net8.0 enable latest enable diff --git a/src/LightExcel/OpenXml/Basic/NodeCollectionXmlPart.cs b/src/LightExcel/OpenXml/Basic/NodeCollectionXmlPart.cs index b6bbe2b..2b7dbd6 100644 --- a/src/LightExcel/OpenXml/Basic/NodeCollectionXmlPart.cs +++ b/src/LightExcel/OpenXml/Basic/NodeCollectionXmlPart.cs @@ -1,36 +1,38 @@ using System.IO.Compression; using LightExcel.OpenXml.Interfaces; using LightExcel.Utils; +using System.Collections; +namespace LightExcel.OpenXml.Basic; -namespace LightExcel.OpenXml +internal abstract class NodeCollectionXmlPart(ZipArchive archive, string path) : XmlPart(archive, path), INodeCollection where T : INode { - internal abstract class NodeCollectionXmlPart : XmlPart, INodeCollection where T : INode - { - - public NodeCollectionXmlPart(ZipArchive archive, string path) : base(archive, path) - { + public List Children { get; } = new List(); - } + public int Count => Children.Count; - public int Count => cached?.Count ?? GetChildren().Count(); + public void AppendChild(T child) => Children.Add(child); - public void AppendChild(T child) - { - cached ??= new List(); - cached.Add(child); - } - public override void Write() + public override void Write() + { + using var writer = archive!.GetWriter(Path); + WriteImpl(writer, Children); + } + protected virtual IEnumerable GetChildren() + { + SetXmlReader(); + if (reader == null) yield break; + foreach (var item in GetChildrenImpl()) { - using var writer = archive!.GetWriter(Path); - WriteImpl(writer, cached?.Cast() ?? Enumerable.Empty()); + yield return item; } + } - private IEnumerable CollectSelfValues() - { - foreach (var item in this) - { - yield return item; - } - } + protected abstract IEnumerable GetChildrenImpl(); + + public virtual IEnumerator GetEnumerator() => GetChildren().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); } -} +} \ No newline at end of file diff --git a/src/LightExcel/OpenXml/Basic/SimpleNodeCollectionXmlPart.cs b/src/LightExcel/OpenXml/Basic/SimpleNodeCollectionXmlPart.cs new file mode 100644 index 0000000..84ab079 --- /dev/null +++ b/src/LightExcel/OpenXml/Basic/SimpleNodeCollectionXmlPart.cs @@ -0,0 +1,20 @@ +using System.Collections; +using LightExcel.OpenXml.Interfaces; + +namespace LightExcel.OpenXml.Basic; + +internal abstract class SimpleNodeCollectionXmlPart : INodeCollection, INode where T : INode +{ + public IEnumerator GetEnumerator() => Children.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + public List Children { get; } = []; + public void AppendChild(T child) => Children.Add(child); + public int Count => Children.Count; + public virtual void WriteToXml(LightExcelStreamWriter writer) + { + + } +} \ No newline at end of file diff --git a/src/LightExcel/OpenXml/Basic/XmlPart.Enumerator.cs b/src/LightExcel/OpenXml/Basic/XmlPart.Enumerator.cs deleted file mode 100644 index 5bd100b..0000000 --- a/src/LightExcel/OpenXml/Basic/XmlPart.Enumerator.cs +++ /dev/null @@ -1,49 +0,0 @@ -using LightExcel.Utils; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; - -namespace LightExcel.OpenXml -{ - internal abstract partial class XmlPart - { - protected virtual LightExcelXmlReader? GetXmlReader() - { - return archive!.GetXmlReader(Path); - } - protected virtual IEnumerable GetChildren() - { - using var reader = GetXmlReader(); - if (reader == null) yield break; - cached ??= new List(); - foreach (var item in GetChildrenImpl(reader)) - { - cached.Add(item); - yield return item; - } - } - - protected abstract IEnumerable GetChildrenImpl(LightExcelXmlReader reader); - - public IEnumerator GetEnumerator() - { - if (cached == null) - { - return GetChildren().GetEnumerator(); - } - else - { - return cached.GetEnumerator(); - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/src/LightExcel/OpenXml/Basic/XmlPart.cs b/src/LightExcel/OpenXml/Basic/XmlPart.cs index a90600b..309d485 100644 --- a/src/LightExcel/OpenXml/Basic/XmlPart.cs +++ b/src/LightExcel/OpenXml/Basic/XmlPart.cs @@ -11,31 +11,39 @@ internal abstract partial class XmlPart : IXmlPart where T : INode { private bool disposedValue; protected ZipArchive? archive; + protected LightExcelXmlReader? reader = null; internal virtual string Path { get; } public XmlPart(ZipArchive archive, string path) { this.archive = archive; Path = path; } - - protected IList? cached; + protected virtual void SetXmlReader() + { + reader ??= archive!.GetXmlReader(Path); + } public virtual void Write() { } - public void Write(IEnumerable children) + public void Write(IEnumerable children) where TNode : INode { using var writer = archive!.GetWriter(Path); WriteImpl(writer, children); } - public void Replace(IEnumerable children) + public void Replace(IEnumerable children) where TNode : INode { - archive!.GetEntry(Path)?.Delete(); + if (reader?.Path == Path) + { + reader?.Dispose(); + reader = null; + archive!.GetEntry(Path)?.Delete(); + } Write(children); } - protected abstract void WriteImpl(LightExcelStreamWriter writer, IEnumerable children); + protected abstract void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) where TNode : INode; protected virtual void Dispose(bool disposing) { @@ -45,6 +53,8 @@ protected virtual void Dispose(bool disposing) { archive?.Dispose(); archive = null; + reader?.Dispose(); + reader = null; } disposedValue = true; } diff --git a/src/LightExcel/OpenXml/Cell.cs b/src/LightExcel/OpenXml/Cell.cs index b6928a9..a4b1ab4 100644 --- a/src/LightExcel/OpenXml/Cell.cs +++ b/src/LightExcel/OpenXml/Cell.cs @@ -1,10 +1,23 @@ using System.Text; using LightExcel.OpenXml.Interfaces; +using LightExcel.Utils; namespace LightExcel.OpenXml { - internal class Cell : INode + internal struct Cell : INode { + public Cell(){} + // public Cell(string? value, string? reference, string? type, string? style) + // { + // Value = value; + // Reference = reference; + // Type = type; + // StyleIndex = style; + // } + // public Cell(string? value, string? reference, string? type, string? style, int? index): this(value, reference, type, style) + // { + // ColumnIndex = index; + // } public string? Reference { get; set; } public string? Type { get; set; } public string? StyleIndex { get; set; } @@ -17,5 +30,14 @@ public void WriteToXml(LightExcelStreamWriter writer) writer.Write($"{Value}"); writer.Write(""); } + + public static Cell EmptyCell(int x, int y) + { + var r = ReferenceHelper.ConvertXyToCellReference(x, y); + return new Cell + { + Reference = r + }; + } } } diff --git a/src/LightExcel/OpenXml/ContentTypes.cs b/src/LightExcel/OpenXml/ContentTypes.cs index c76c922..e427767 100644 --- a/src/LightExcel/OpenXml/ContentTypes.cs +++ b/src/LightExcel/OpenXml/ContentTypes.cs @@ -1,4 +1,5 @@ -using LightExcel.OpenXml.Interfaces; +using LightExcel.OpenXml.Basic; +using LightExcel.OpenXml.Interfaces; using LightExcel.Utils; using System.IO.Compression; using System.Text; @@ -17,8 +18,9 @@ public ContentTypes(ZipArchive archive) : base(archive, "[Content_Types].xml") } - protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader) + protected override IEnumerable GetChildrenImpl() { + if (reader is null) yield break; if (!reader.IsStartWith("Types", XmlHelper.ContentTypesXmlns)) yield break; if (!reader.ReadFirstContent()) yield break; @@ -31,12 +33,12 @@ protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader var ov = new Override(pn, ct); yield return ov; } - else if (!reader.SkipContent()) + else if (!reader.SkipContent()) yield break; } } - protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) + protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) { writer.Write(""); writer.Write(""); diff --git a/src/LightExcel/OpenXml/Interfaces/INodeCollection.cs b/src/LightExcel/OpenXml/Interfaces/INodeCollection.cs index 74bf380..dd6b5c7 100644 --- a/src/LightExcel/OpenXml/Interfaces/INodeCollection.cs +++ b/src/LightExcel/OpenXml/Interfaces/INodeCollection.cs @@ -1,7 +1,8 @@ namespace LightExcel.OpenXml.Interfaces { - internal interface INodeCollection where T : INode + internal interface INodeCollection : IEnumerable where T : INode { + List Children { get; } void AppendChild(T child); int Count { get; } } diff --git a/src/LightExcel/OpenXml/Interfaces/IXmlPart.cs b/src/LightExcel/OpenXml/Interfaces/IXmlPart.cs index 8509a65..8b4f6db 100644 --- a/src/LightExcel/OpenXml/Interfaces/IXmlPart.cs +++ b/src/LightExcel/OpenXml/Interfaces/IXmlPart.cs @@ -1,9 +1,9 @@ namespace LightExcel.OpenXml.Interfaces { - internal interface IXmlPart : IDisposable, IEnumerable where T : INode + internal interface IXmlPart : IDisposable where T : INode { void Write(); - void Write(IEnumerable children); - void Replace(IEnumerable children); + void Write(IEnumerable children) where TNode : INode; + void Replace(IEnumerable children) where TNode : INode; } } diff --git a/src/LightExcel/OpenXml/LightExcelXmlReader.cs b/src/LightExcel/OpenXml/LightExcelXmlReader.cs index c827086..4692dd6 100644 --- a/src/LightExcel/OpenXml/LightExcelXmlReader.cs +++ b/src/LightExcel/OpenXml/LightExcelXmlReader.cs @@ -1,89 +1,92 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; +using System.Xml; -namespace LightExcel.OpenXml +namespace LightExcel.OpenXml; + +//internal static class LightExcelXmlReaderExtensions +//{ +// public static bool IsStartWith(this LightExcelXmlReader reader, string elementName, params string[] xmlns) +// { +// return xmlns.Any(ns => reader.Reader.IsStartElement(elementName, ns)); +// } +//} +internal class LightExcelXmlReader : IDisposable { - internal class LightExcelXmlReader : IDisposable - { - public LightExcelXmlReader(Stream stream) - { - this.stream = stream; - this.reader = XmlReader.Create(stream); - } + public LightExcelXmlReader(Stream stream, string path) + { + this.stream = stream; + Path = path; + this.reader = XmlReader.Create(stream); + } - private bool disposedValue; - private readonly Stream stream; - private XmlReader reader; + private bool disposedValue; + private readonly Stream stream; + private XmlReader reader; - public bool EOF => reader.EOF; - public XmlReader Reader => reader; - public string? this[string name] => GetAttribute(name); - public string? this[string name, string ns] => reader.GetAttribute(name, ns); - public string? GetAttribute(string name) => reader.GetAttribute(name); - public string? GetAttribute(string name, string ns) => reader.GetAttribute(name, ns); + public bool EOF => reader.EOF; + public string Path { get; } + public XmlReader Reader => reader; + public string? this[string name] => GetAttribute(name); + public string? this[string name, string ns] => reader.GetAttribute(name, ns); + public string? GetAttribute(string name) => reader.GetAttribute(name); + public string? GetAttribute(string name, string ns) => reader.GetAttribute(name, ns); - public string ReadElementContentAsString() => reader.ReadElementContentAsString(); + public string ReadElementContentAsString() => reader.ReadElementContentAsString(); - public bool IsStartWith(string elementName, params string[] xmlns) - { - return xmlns.Any(ns => reader.IsStartElement(elementName, ns)); - } + public bool IsStartWith(string elementName, params string[] xmlns) + { + return xmlns.Any(ns => reader.IsStartElement(elementName, ns)); + } - public bool ReadFirstContent() + public bool ReadFirstContent() + { + if (reader.IsEmptyElement) { - if (reader.IsEmptyElement) - { - reader.Read(); - return false; - } - reader.MoveToContent(); reader.Read(); - return true; + return false; } + reader.MoveToContent(); + reader.Read(); + return true; + } - public void SkipNextSibling() + public void SkipNextSibling() + { + while (!reader.EOF) { - while (!reader.EOF) - { - if (!SkipContent()) - break; - } + if (!SkipContent()) + break; } + } - public bool SkipContent() + public bool SkipContent() + { + if (reader.NodeType == XmlNodeType.EndElement) { - if (reader.NodeType == XmlNodeType.EndElement) - { - reader.Read(); - return false; - } - - reader.Skip(); - return true; + reader.Read(); + return false; } - protected virtual void Dispose(bool disposing) + reader.Skip(); + return true; + } + + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) { - if (!disposedValue) + if (disposing) { - if (disposing) - { - reader?.Dispose(); - stream?.Dispose(); - } - disposedValue = true; + reader?.Dispose(); + stream?.Dispose(); } + disposedValue = true; } + } - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); } } diff --git a/src/LightExcel/OpenXml/MergeCellCollection.cs b/src/LightExcel/OpenXml/MergeCellCollection.cs index 9be691f..eab4fc7 100644 --- a/src/LightExcel/OpenXml/MergeCellCollection.cs +++ b/src/LightExcel/OpenXml/MergeCellCollection.cs @@ -1,25 +1,20 @@ using LightExcel.OpenXml.Interfaces; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using LightExcel.OpenXml.Basic; namespace LightExcel.OpenXml { - internal class MergeCellCollection : INodeCollection, INode + internal class MergeCellCollection : SimpleNodeCollectionXmlPart { - public int Count => _cells.Count; - IList _cells = new List(); - public void AppendChild(MergeCell child) - { - _cells.Add(child); - } - - public void WriteToXml(LightExcelStreamWriter writer) + public override void WriteToXml(LightExcelStreamWriter writer) { writer.Write($""); - foreach (var item in _cells) + foreach (var item in Children) { item.WriteToXml(writer); } @@ -27,13 +22,10 @@ public void WriteToXml(LightExcelStreamWriter writer) } } - internal class MergeCell : INode + internal class MergeCell(string mergeref) : INode { - public string MergeRef { get; set; } - public MergeCell(string mergeref) - { - MergeRef = mergeref; - } + public string MergeRef { get; set; } = mergeref; + public void WriteToXml(LightExcelStreamWriter writer) { writer.Write($""); diff --git a/src/LightExcel/OpenXml/RelationshipCollection.cs b/src/LightExcel/OpenXml/RelationshipCollection.cs index 22b611c..10081da 100644 --- a/src/LightExcel/OpenXml/RelationshipCollection.cs +++ b/src/LightExcel/OpenXml/RelationshipCollection.cs @@ -1,4 +1,5 @@ -using LightExcel.OpenXml.Interfaces; +using LightExcel.OpenXml.Basic; +using LightExcel.OpenXml.Interfaces; using LightExcel.Utils; using System.Collections; using System.IO.Compression; @@ -7,15 +8,11 @@ namespace LightExcel.OpenXml { - internal class RelationshipCollection : NodeCollectionXmlPart + internal class RelationshipCollection(ZipArchive archive) : NodeCollectionXmlPart(archive, "xl/_rels/workbook.xml.rels") { - public RelationshipCollection(ZipArchive archive) : base(archive, "xl/_rels/workbook.xml.rels") - { - - } - - protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader) + protected override IEnumerable GetChildrenImpl() { + if (reader is null) yield break; if (!reader.IsStartWith("Relationships", XmlHelper.RelaNs)) yield break; if (!reader.ReadFirstContent()) yield break; while (!reader.EOF) @@ -31,7 +28,7 @@ protected override IEnumerable GetChildrenImpl(LightExcelXmlReader } } - protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) + protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) { writer.Write(""); writer.Write(""); @@ -41,6 +38,5 @@ protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable"); } - } } diff --git a/src/LightExcel/OpenXml/RowCollection.cs b/src/LightExcel/OpenXml/RowCollection.cs index a2c84f9..0a9235f 100644 --- a/src/LightExcel/OpenXml/RowCollection.cs +++ b/src/LightExcel/OpenXml/RowCollection.cs @@ -1,29 +1,41 @@ -using LightExcel.OpenXml.Interfaces; +using System.Collections; +using LightExcel.OpenXml.Basic; +using LightExcel.OpenXml.Interfaces; +using LightExcel.Utils; namespace LightExcel.OpenXml { - internal class Row : INodeCollection, INode + internal static class RowExtensions + { + public static void AddAndFixed(this Row row, Cell child) + { + var (x, y) = ReferenceHelper.ConvertCellReferenceToXY(child.Reference); + while (row.Children.Count < x - 1 && x.HasValue && y.HasValue) + { + row.Children.Add(Cell.EmptyCell(row.Children.Count + 1, y.Value)); + } + row.Children.Add(child); + } + } + + internal class Row : SimpleNodeCollectionXmlPart { public int RowIndex { get; set; } public bool IsTemplateRow { get; set; } - public List RowDatas { get; set; } = new List(); - - public int Count => RowDatas.Count; - public void AppendChild(Cell child) + public override void WriteToXml(LightExcelStreamWriter writer) { - RowDatas.Add(child); - } + if (IsTemplateRow) + { + return; + } - public void WriteToXml(LightExcelStreamWriter writer) - { - if (IsTemplateRow) { return; } writer.Write($""); - foreach (Cell cell in RowDatas) + foreach (Cell cell in Children) { cell.WriteToXml(writer); } writer.Write(""); } } -} +} \ No newline at end of file diff --git a/src/LightExcel/OpenXml/SharedStringTable.cs b/src/LightExcel/OpenXml/SharedStringTable.cs index 317ba17..cad84fc 100644 --- a/src/LightExcel/OpenXml/SharedStringTable.cs +++ b/src/LightExcel/OpenXml/SharedStringTable.cs @@ -1,4 +1,5 @@ -using LightExcel.OpenXml.Interfaces; +using LightExcel.OpenXml.Basic; +using LightExcel.OpenXml.Interfaces; using LightExcel.Utils; using System.IO.Compression; using System.Xml; @@ -12,34 +13,42 @@ internal class SharedStringTable : NodeCollectionXmlPart { public int RefCount { get; set; } public int UniqueCount { get; set; } + //private readonly IDictionary values = new Dictionary(); + public SharedStringTable(ZipArchive archive) : base(archive, "xl/sharedStrings.xml") { - + Flush(); } internal string? this[int index] { get { - if (cached == null) - { - Flush(); - } - return cached![index].Content; + if (index < 0 || index >= Children.Count) + return null; + return Children[index].Content; } } private void Flush() { - _ = GetChildren().ToList(); + // _ = GetChildren().ToList(); + Children.Clear(); + foreach (var s in GetChildren()) + { + Children.Add(s); + } } - protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader) + public override IEnumerator GetEnumerator() => Children.GetEnumerator(); + + protected override IEnumerable GetChildrenImpl() { + if (reader is null) yield break; if (!reader.IsStartWith("sst", XmlHelper.MainNs)) yield break; if (!reader.ReadFirstContent()) yield break; - int.TryParse(reader["count"], out var count); - int.TryParse(reader["uniqueCount"], out var uniqueCount); + _ = int.TryParse(reader["count"], out var count); + _ = int.TryParse(reader["uniqueCount"], out var uniqueCount); RefCount = count; UniqueCount = uniqueCount; while (!reader.EOF) @@ -56,7 +65,7 @@ protected override IEnumerable GetChildrenImpl(LightExcelXmlRe } } - protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) + protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) { writer.Write(""); writer.Write($""); @@ -64,7 +73,8 @@ protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable"); } } -} +} \ No newline at end of file diff --git a/src/LightExcel/OpenXml/Sheet.cs b/src/LightExcel/OpenXml/Sheet.cs index b8facfe..31c4872 100644 --- a/src/LightExcel/OpenXml/Sheet.cs +++ b/src/LightExcel/OpenXml/Sheet.cs @@ -1,4 +1,5 @@ -using LightExcel.OpenXml.Interfaces; +using LightExcel.OpenXml.Basic; +using LightExcel.OpenXml.Interfaces; using LightExcel.Utils; using System.IO.Compression; using System.Net; @@ -9,7 +10,7 @@ namespace LightExcel.OpenXml /// /// xl/worksheets/sheet{id}.xml /// - internal class Sheet : XmlPart, INode + internal class Sheet : NodeCollectionXmlPart, INode { public Sheet(ZipArchive archive, string id, string name, int sid) : base(archive, "") { @@ -18,6 +19,7 @@ public Sheet(ZipArchive archive, string id, string name, int sid) : base(archive Name = name; SheetIdx = sid; } + public Sheet(ZipArchive archive, string name, int index) : base(archive, "") { Id = $"R{Guid.NewGuid():N}"; @@ -26,34 +28,33 @@ public Sheet(ZipArchive archive, string name, int index) : base(archive, "") } public string Id { get; set; } + /// /// 在集合中的顺序(保底机制 /// public int Seq { get; set; } + public string? Name { get; set; } public int SheetIdx { get; set; } public bool NeedToSave { get; set; } = true; internal override string Path => $"xl/worksheets/sheet{SheetIdx}.xml"; public string RelPath => $"worksheets/sheet{SheetIdx}.xml"; - protected override LightExcelXmlReader? GetXmlReader() + protected override void SetXmlReader() { - var reader = base.GetXmlReader(); - if (reader == null) - { - reader = archive?.GetXmlReader($"xl/worksheets/sheet{Seq + 1}.xml"); - } - return reader; + reader ??= archive?.GetXmlReader($"xl/worksheets/sheet{Seq + 1}.xml"); } - public ExcelColumnInfo[] Columns { get; set; } + + public ExcelColumnInfo[] Columns { get; set; } = []; public int MaxColumnIndex { get; set; } public int MaxRowIndex { get; set; } + public void WriteToXml(LightExcelStreamWriter writer) { writer.Write($""""""); } - protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) + protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) { writer.Write(""); writer.Write(""); @@ -65,7 +66,7 @@ protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable"); - foreach (INode child in children) + foreach (var child in children) { child.WriteToXml(writer); } @@ -92,8 +93,9 @@ private void WriteMergeInfo(LightExcelStreamWriter writer) internal MergeCellCollection? MergeCells { get; set; } internal string? ColInfos { get; set; } - protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader) + protected override IEnumerable GetChildrenImpl() { + if (reader is null) yield break; if (!reader.IsStartWith("worksheet", XmlHelper.MainNs)) yield break; if (!reader.ReadFirstContent()) @@ -124,13 +126,14 @@ protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader) StyleIndex = reader.GetAttribute("s"), }; cell.Value = ReadCellValue(reader); - row.RowDatas.Add(cell); + row.AddAndFixed(cell); } else if (!reader.SkipContent()) { break; } } + yield return row; } else if (!reader.SkipContent()) @@ -198,10 +201,9 @@ private static void CollectCols(Sheet sheet) { return; } - - var cols = sheet.Columns.Where(c => c.Width.HasValue).Select(c => + var cols = sheet.Columns.Where(c => c.Width.HasValue).Select(c => $""""""); - sheet.ColInfos = $"{string.Join("",cols)}"; + sheet.ColInfos = $"{string.Join("", cols)}"; } private static int ReserveColsSpace(Sheet sheet) @@ -211,26 +213,23 @@ private static int ReserveColsSpace(Sheet sheet) return sheet.ColInfos!.Length; } - return 13 + sheet.Columns.Where(c => c.Width.HasValue || c.AutoWidth).Sum(c => - { - // 13 {Columns}.Max(ColumnIndex).Length * {Columns}.Length * 2 + 预留宽度(10个字符=>最大宽度 9999999999) - // - // 41 - // - return 41 + StringLen(c.ColumnIndex) * 2 + 10; - }); + // 13 {Columns}.Max(ColumnIndex).Length * {Columns}.Length * 2 + 预留宽度(10个字符=>最大宽度 9999999999) + // + // 41 + // + return 13 + sheet.Columns.Where(c => c.Width.HasValue || c.AutoWidth).Sum(c => 41 + StringLen(c.ColumnIndex) * 2 + 10); static int StringLen(int val) { int len = 1; - while(val >= 10) + while (val >= 10) { val /= 10; len++; } + return len; } } } - -} +} \ No newline at end of file diff --git a/src/LightExcel/OpenXml/SheetCollection.cs b/src/LightExcel/OpenXml/SheetCollection.cs index aef6ef9..9359157 100644 --- a/src/LightExcel/OpenXml/SheetCollection.cs +++ b/src/LightExcel/OpenXml/SheetCollection.cs @@ -1,4 +1,5 @@ -using LightExcel.OpenXml.Interfaces; +using LightExcel.OpenXml.Basic; +using LightExcel.OpenXml.Interfaces; using LightExcel.Utils; using System.IO.Compression; @@ -16,7 +17,7 @@ public SheetCollection(ZipArchive archive, ExcelConfiguration configuration) this.configuration = configuration; } - protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader) + protected override IEnumerable GetChildrenImpl() { if (!reader.IsStartWith("workbook", XmlHelper.MainNs)) yield break; if (!reader.ReadFirstContent()) yield break; @@ -57,12 +58,12 @@ protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader } } - protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) + protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) { writer.Write(""); writer.Write(""); writer.Write(""); - foreach (INode child in children) + foreach (var child in children) { child.WriteToXml(writer); } diff --git a/src/LightExcel/OpenXml/SheetCols.cs b/src/LightExcel/OpenXml/SheetCols.cs index 6ecc2a1..7c3f151 100644 --- a/src/LightExcel/OpenXml/SheetCols.cs +++ b/src/LightExcel/OpenXml/SheetCols.cs @@ -1,9 +1,11 @@ using LightExcel.OpenXml.Interfaces; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using LightExcel.OpenXml.Basic; namespace LightExcel.OpenXml { @@ -20,19 +22,12 @@ public void WriteToXml(LightExcelStreamWriter writer) writer.Write($""); } } - internal class SheetCols : INodeCollection,INode + internal class SheetCols : SimpleNodeCollectionXmlPart { - public int Count => Cols.Count; - List Cols = new List(); - public void AppendChild(Col child) - { - Cols.Add(child); - } - - public void WriteToXml(LightExcelStreamWriter writer) + public override void WriteToXml(LightExcelStreamWriter writer) { writer.Write(""); - foreach (var item in Cols) + foreach (var item in Children) { item.WriteToXml(writer); } diff --git a/src/LightExcel/OpenXml/StyleSheet.cs b/src/LightExcel/OpenXml/StyleSheet.cs index 62f3975..e689401 100644 --- a/src/LightExcel/OpenXml/StyleSheet.cs +++ b/src/LightExcel/OpenXml/StyleSheet.cs @@ -1,4 +1,5 @@ -using LightExcel.OpenXml.Interfaces; +using LightExcel.OpenXml.Basic; +using LightExcel.OpenXml.Interfaces; using LightExcel.OpenXml.Styles; using System.IO.Compression; @@ -16,12 +17,12 @@ public StyleSheet(ZipArchive archive) : base(archive, "xl/styles.xml") public NumberingFormatCollection? NumberingFormats { get; set; } public CellFormatCollection? CellFormats { get; set; } - protected override IEnumerable GetChildrenImpl(LightExcelXmlReader reader) + protected override IEnumerable GetChildrenImpl() { throw new NotImplementedException(); } - protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) + protected override void WriteImpl(LightExcelStreamWriter writer, IEnumerable children) { writer.Write(""); writer.Write(""); diff --git a/src/LightExcel/OpenXml/Styles/BorderCollection.cs b/src/LightExcel/OpenXml/Styles/BorderCollection.cs index 19bb8e3..1fd29ad 100644 --- a/src/LightExcel/OpenXml/Styles/BorderCollection.cs +++ b/src/LightExcel/OpenXml/Styles/BorderCollection.cs @@ -1,21 +1,15 @@ -using LightExcel.OpenXml.Interfaces; +using System.Collections; +using LightExcel.OpenXml.Basic; +using LightExcel.OpenXml.Interfaces; namespace LightExcel.OpenXml.Styles { - internal class BorderCollection : INodeCollection, INode + internal class BorderCollection : SimpleNodeCollectionXmlPart { - internal IList Borders { get; set; } = new List(); - public int Count => Borders.Count; - - public void AppendChild(Border child) - { - Borders.Add(child); - } - - public void WriteToXml(LightExcelStreamWriter writer) + public override void WriteToXml(LightExcelStreamWriter writer) { writer.Write($""); - foreach (var item in Borders) + foreach (var item in Children) { item.WriteToXml(writer); } diff --git a/src/LightExcel/OpenXml/Styles/FontCollection.cs b/src/LightExcel/OpenXml/Styles/FontCollection.cs index ffe1550..036370a 100644 --- a/src/LightExcel/OpenXml/Styles/FontCollection.cs +++ b/src/LightExcel/OpenXml/Styles/FontCollection.cs @@ -4,17 +4,13 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using LightExcel.OpenXml.Basic; namespace LightExcel.OpenXml.Styles { - internal class FontCollection : INodeCollection + internal class FontCollection : SimpleNodeCollectionXmlPart { - public int Count => throw new NotImplementedException(); - - public void AppendChild(Font child) - { - throw new NotImplementedException(); - } + } internal class Font : INode diff --git a/src/LightExcel/Renders/DictionaryRender.cs b/src/LightExcel/Renders/DictionaryRender.cs index d0088f1..fab98aa 100644 --- a/src/LightExcel/Renders/DictionaryRender.cs +++ b/src/LightExcel/Renders/DictionaryRender.cs @@ -15,9 +15,11 @@ public override IEnumerable CollectExcelColumnInfo(object data) int index = 1; foreach (var item in d.First().Keys) { - var col = new ExcelColumnInfo(item); - col.NumberFormat = Configuration.CheckCellNumberFormat(item); - col.ColumnIndex = index++; + var col = new ExcelColumnInfo(item) + { + NumberFormat = Configuration.CheckCellNumberFormat(item), + ColumnIndex = index++ + }; AssignDynamicInfo(col); yield return col; } diff --git a/src/LightExcel/Utils/CellHelper.cs b/src/LightExcel/Utils/CellHelper.cs index ad1299c..a32f5b3 100644 --- a/src/LightExcel/Utils/CellHelper.cs +++ b/src/LightExcel/Utils/CellHelper.cs @@ -188,7 +188,7 @@ internal static double CalcStringWidth(string value) internal static string? GetCellValue(this Cell cell, SharedStringTable? table) { - if (cell == null) return null; + // if (cell == null) return null; if (cell.Type == "s") { if (int.TryParse(cell.Value, out var s)) diff --git a/src/LightExcel/Utils/ReferenceHelper.cs b/src/LightExcel/Utils/ReferenceHelper.cs index f2e978d..122fefb 100644 --- a/src/LightExcel/Utils/ReferenceHelper.cs +++ b/src/LightExcel/Utils/ReferenceHelper.cs @@ -54,7 +54,7 @@ public static (int? X, int? Y) ConvertCellReferenceToXY(string? cellref) return (x, y); } - private static int GetRowIndex(string cellref) + public static int GetRowIndex(string cellref) { var num = string.Empty; foreach (var c in cellref) @@ -67,7 +67,7 @@ private static int GetRowIndex(string cellref) return int.Parse(num); } - private static int GetColumnIndex(string cellref) + public static int GetColumnIndex(string cellref) { var x = 0; foreach (var c in cellref) diff --git a/src/LightExcel/Utils/XmlHelper.cs b/src/LightExcel/Utils/XmlHelper.cs index 99f20e9..7be667a 100644 --- a/src/LightExcel/Utils/XmlHelper.cs +++ b/src/LightExcel/Utils/XmlHelper.cs @@ -14,9 +14,11 @@ internal static class XmlHelper { public static LightExcelXmlReader? GetXmlReader(this ZipArchive archive, string path) { + System.Diagnostics.Debug.WriteLine($"opening {path}"); var stream = archive.GetEntry(path)?.Open(); + System.Diagnostics.Debug.WriteLine($"opened {path}"); if (stream == null) return null; - return new LightExcelXmlReader(stream); + return new LightExcelXmlReader(stream, path); } public static LightExcelStreamWriter GetWriter(this ZipArchive archive, string path) diff --git a/test/TestProject1/OpenXmlExcelReadTest.cs b/test/TestProject1/OpenXmlExcelReadTest.cs index a041e83..d0621eb 100644 --- a/test/TestProject1/OpenXmlExcelReadTest.cs +++ b/test/TestProject1/OpenXmlExcelReadTest.cs @@ -31,7 +31,7 @@ public void ExcelReaderTest() public void ExcelReaderTestEntity() { ExcelHelper excel = new ExcelHelper(); - var result = excel.QueryExcel("etest.xlsx","Sheet1"); + var result = excel.QueryExcel("etest.xlsx", "Sheet1"); } [TestMethod] public void ExcelReaderTestDynamic() @@ -56,20 +56,29 @@ public void ExcelReaderTestDynamic() public void ExcelReaderTestDynamic2() { ExcelHelper excel = new ExcelHelper(); + int actionCount = 0; + int totalRowCount = 0; + int notMapCount = 0; var reader = excel.ReadExcel("C:\\Users\\Marvel\\Desktop\\驾驶人证件过期短信提醒\\20250416模板\\1驾驶人临近期满换证(期满日期前3个月)_结果.xlsx"); while (reader.NextResult()) { while (reader.Read()) { - //Console.WriteLine($"Index: {reader.RowIndex}, F: {reader[5]}, H: {reader[7]}"); - _ = $"Index: {reader.RowIndex}, F: {reader[5]}, H: {reader[7]}"; + var kx = reader.GetValue(5)?.ToString(); + var yz = reader.GetValue(7)?.ToString(); + //rows.Add($"{sfz}-{sj}-{sj2}-{kx}-{yz}"); + if (kx?.Contains("是") == true && yz?.Contains("否") == true) + { + actionCount++; + } + else + { + notMapCount++; + } + totalRowCount++; } } - //var resule = excel.QueryExcel("C:\\Users\\Marvel\\Desktop\\截止20231017二期车证.xlsx"); - //foreach (var field in result) - //{ - // Console.WriteLine($"D: {field.D}, E: {field.E}"); - //} + Console.WriteLine($"已读行数: {totalRowCount} , 符合条件的数量: {actionCount}/{notMapCount}"); } } } \ No newline at end of file From 49ae318fb9ad868486e15597af2304f3866f9103 Mon Sep 17 00:00:00 2001 From: MarvelTiter_yaoqinglin Date: Thu, 22 May 2025 16:37:15 +0800 Subject: [PATCH 2/2] 1 --- src/LightExcel/LightExcel.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/LightExcel/LightExcel.csproj b/src/LightExcel/LightExcel.csproj index 58076c2..e945a5c 100644 --- a/src/LightExcel/LightExcel.csproj +++ b/src/LightExcel/LightExcel.csproj @@ -9,11 +9,9 @@ True MT.LightExcel 基于OpenXml的Excel读取与写入 - 2.1.2 - 2.1.2 + 2.1.3 https://github.com/MarvelTiter/LightExcel.git MIT - 2.1.2 Nuget.md