Web Analytics

MiniExcel

⭐ 3149 stars Arabic by mini-software

NuGet Build status star GitHub stars version Ask DeepWiki


هذا المشروع هو جزء من مؤسسة .NET ويعمل تحت مدونة السلوك الخاصة بهم.


English | 简体中文 | 繁體中文 | 日本語 | 한국어 | हिन्दी | ไทย | Français | Deutsch | Español | Italiano | Русский | Português | Nederlands | Polski | العربية | فارسی | Türkçe | Tiếng Việt | Bahasa Indonesia


إن نجماتك أو تبرعاتك يمكن أن تجعل MiniExcel أفضل


مقدمة

MiniExcel هو أداة لمعالجة ملفات Excel بسيطة وفعالة لمنصة .NET، مصممة خصيصًا لتقليل استخدام الذاكرة.

في الوقت الحالي، تتطلب معظم الأطر الشائعة تحميل جميع البيانات من مستند Excel إلى الذاكرة لتسهيل العمليات، ولكن قد يؤدي ذلك إلى مشاكل استهلاك الذاكرة. نهج MiniExcel مختلف: تتم معالجة البيانات صفًا بصف بطريقة تدفقية، مما يقلل الاستهلاك الأصلي من مئات الميجابايتات المحتملة إلى عدة ميجابايتات فقط، مما يمنع بشكل فعال مشكلات نفاد الذاكرة (OOM).

flowchart LR
    A1(["Excel analysis
process"]) --> A2{{"Unzipping
XLSX file"}} --> A3{{"Parsing
OpenXML"}} --> A4{{"Model
conversion"}} --> A5(["Output"])

B1(["Other Excel
Frameworks"]) --> B2{{"Memory"}} --> B3{{"Memory"}} --> B4{{"Workbooks &
Worksheets"}} --> B5(["All rows at
the same time"])

C1(["MiniExcel"]) --> C2{{"Stream"}} --> C3{{"Stream"}} --> C4{{"POCO or dynamic"}} --> C5(["Deferred execution
row by row"])

classDef analysis fill:#D0E8FF,stroke:#1E88E5,color:#0D47A1,font-weight:bold; classDef others fill:#FCE4EC,stroke:#EC407A,color:#880E4F,font-weight:bold; classDef miniexcel fill:#E8F5E9,stroke:#388E3C,color:#1B5E20,font-weight:bold;

class A1,A2,A3,A4,A5 analysis; class B1,B2,B3,B4,B5 others; class C1,C2,C3,C4,C5 miniexcel;

الميزات

ابدأ الآن

التثبيت

يمكنك تثبيت الحزمة من NuGet

ملاحظات الإصدار

يرجى مراجعة ملاحظات الإصدار

المهام القادمة

يرجى مراجعة المهام القادمة

الأداء

يمكنك العثور على كود اختبارات الأداء في MiniExcel.Benchmarks.

الملف المستخدم لاختبار الأداء هو Test1,000,000x10.xlsx، وهو مستند بحجم 32 ميغابايت يحتوي على 1,000,000 صف * 10 أعمدة، تم ملء خلاياه بالنص "HelloWorld".

لتشغيل جميع اختبارات الأداء استخدم:

dotnet run -project .\benchmarks\MiniExcel.Benchmarks -c Release -f net9.0 -filter * --join
يمكنك العثور على نتائج اختبارات الأداء للإصدار الأخير هنا.

استعلام/استيراد Excel

#### 1. تنفيذ استعلام وربط النتائج مع IEnumerable ذو نوع قوي [[جرّب الآن]](https://dotnetfiddle.net/w5WD1J)

يوصى باستخدام Stream.Query لما يتمتع به من كفاءة أفضل.

public class UserAccount
{
    public Guid ID { get; set; }
    public string Name { get; set; }
    public DateTime BoD { get; set; }
    public int Age { get; set; }
    public bool VIP { get; set; }
    public decimal Points { get; set; }
}

var rows = MiniExcel.Query(path);

// or

using (var stream = File.OpenRead(path)) var rows = stream.Query();

image

#### 2. تنفيذ استعلام وتعيينه إلى قائمة من الكائنات الديناميكية بدون استخدام الرأس [[جرّب ذلك]](https://dotnetfiddle.net/w5WD1J)

| MiniExcel | 1 | |-----------|---| | Github | 2 |


var rows = MiniExcel.Query(path).ToList();

// or using (var stream = File.OpenRead(path)) { var rows = stream.Query().ToList();

Assert.Equal("MiniExcel", rows[0].A); Assert.Equal(1, rows[0].B); Assert.Equal("Github", rows[1].A); Assert.Equal(2, rows[1].B); }

#### 3. تنفيذ استعلام مع أول صف كعنوان [[جرّب ذلك]](https://dotnetfiddle.net/w5WD1J)

ملاحظة: في حال تكرار اسم العمود، يتم استخدام الأخير من اليمين

ملف إكسل المدخل:

| العمود1 | العمود2 | |-----------|---------| | MiniExcel | 1 | | Github | 2 |


var rows = MiniExcel.Query(useHeaderRow:true).ToList();

// or

using (var stream = File.OpenRead(path)) { var rows = stream.Query(useHeaderRow:true).ToList();

Assert.Equal("MiniExcel", rows[0].Column1); Assert.Equal(1, rows[0].Column2); Assert.Equal("Github", rows[1].Column1); Assert.Equal(2, rows[1].Column2); }

#### 4. دعم استعلام امتدادات LINQ مثل First/Take/Skip ...إلخ

استعلام First

var row = MiniExcel.Query(path).First();
Assert.Equal("HelloWorld", row.A);

// or

using (var stream = File.OpenRead(path)) { var row = stream.Query().First(); Assert.Equal("HelloWorld", row.A); }

الأداء بين MiniExcel/ExcelDataReader/ClosedXML/EPPlus queryfirst

#### 5. الاستعلام حسب اسم الورقة

MiniExcel.Query(path, sheetName: "SheetName");
//or
stream.Query(sheetName: "SheetName");
#### 6. استعلام عن جميع أسماء الأوراق والصفوف

var sheetNames = MiniExcel.GetSheetNames(path);
foreach (var sheetName in sheetNames)
{
    var rows = MiniExcel.Query(path, sheetName: sheetName);
}
#### 7. الحصول على الأعمدة

var columns = MiniExcel.GetColumns(path); // e.g result : ["A","B"...]

var cnt = columns.Count; // get column count

#### 8. استعلام ديناميكي تحويل الصف إلى IDictionary

foreach(IDictionary row in MiniExcel.Query(path))
{
    //..
}

// or var rows = MiniExcel.Query(path).Cast>(); // or Query specified ranges (capitalized) // A2 represents the second row of column A, C3 represents the third row of column C // If you don't want to restrict rows, just don't include numbers var rows = MiniExcel.QueryRange(path, startCell: "A2", endCell: "C3").Cast>();

#### 9. استعلام إكسل يرجع DataTable

غير مستحسن، لأن DataTable سيقوم بتحميل جميع البيانات في الذاكرة ويفقد ميزة الاستهلاك المنخفض للذاكرة في MiniExcel.

``C# var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);

image

#### 10. تحديد الخلية لبدء قراءة البيانات

csharp MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
image

#### 11. تعبئة الخلايا المدمجة

ملاحظة: الكفاءة أبطأ مقارنة بـ عدم استخدام تعبئة الدمج

السبب: معيار OpenXml يضع mergeCells في أسفل الملف، مما يؤدي إلى الحاجة لتكرار المرور على sheetxml مرتين

csharp var config = new OpenXmlConfiguration() { FillMergedCells = true }; var rows = MiniExcel.Query(path, configuration: config);
image

يدعم الطول والعرض المتغيرين وملء متعدد الصفوف والأعمدة

image

#### 12. قراءة الملفات الكبيرة باستخدام التخزين المؤقت على القرص (Disk-Base Cache - SharedString)

إذا تجاوز حجم SharedStrings ‏5 ميجابايت، فإن MiniExcel بشكل افتراضي سيستخدم التخزين المؤقت المحلي على القرص، مثل 10x100000.xlsx (بيانات مليون صف)، عند تعطيل التخزين المؤقت للقرص يكون الحد الأقصى لاستخدام الذاكرة 195 ميجابايت، ولكن عند تفعيل التخزين المؤقت للقرص يحتاج فقط إلى 65 ميجابايت. ملاحظة، يتطلب هذا التحسين بعض التكاليف في الكفاءة، لذا في هذه الحالة سيزيد وقت القراءة من 7.4 ثانية إلى 27.2 ثانية، إذا لم تكن بحاجة إليه يمكنك تعطيل التخزين المؤقت للقرص باستخدام الكود التالي:

csharp var config = new OpenXmlConfiguration { EnableSharedStringCache = false }; MiniExcel.Query(path,configuration: config)
يمكنك استخدام SharedStringCacheSize لتغيير حجم ملف sharedString بحيث يتجاوز الحجم المحدد لتخزين البيانات مؤقتًا على القرص
csharp var config = new OpenXmlConfiguration { SharedStringCacheSize=50010241024 }; MiniExcel.Query(path, configuration: config);
image

image

إنشاء/تصدير إكسل

  • يجب أن يكون نوعًا غير مجرد مع مُنشئ عام بدون معلمات.
  • يدعم MiniExcel تنفيذ IEnumerable المؤجل، إذا كنت ترغب في استخدام أقل قدر من الذاكرة، يرجى عدم استدعاء طرق مثل ToList
مثال: استخدام أو عدم استخدام ToList واستهلاك الذاكرة image

#### 1. مجهول النوع أو نوع قوي [[جربه]](https://dotnetfiddle.net/w5WD1J)

csharp var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); MiniExcel.SaveAs(path, new[] { new { Column1 = "MiniExcel", Column2 = 1 }, new { Column1 = "Github", Column2 = 2} });
#### 2. IEnumerable>
#### 2. IEnumerable>
#### 2. IEnumerable>
csharp var values = new List>() { new Dictionary{{ "Column1", "MiniExcel" }, { "Column2", 1 } }, new Dictionary{{ "Column1", "Github" }, { "Column2", 2 } } }; MiniExcel.SaveAs(path, values);
إنشاء نتيجة الملف :

| العمود1 | العمود2 | |-----------|---------| | MiniExcel | 1 | | Github | 2 |

#### 3. IDataReader

  • موصى به، يمكنه تجنب تحميل جميع البيانات في الذاكرة
csharp MiniExcel.SaveAs(path, reader);
image

تصدير أوراق متعددة باستخدام DataReader (موصى به بواسطة Dapper ExecuteReader)

csharp using (var cnn = Connection) { cnn.Open(); var sheets = new Dictionary(); sheets.Add("sheet1", cnn.ExecuteReader("select 1 id")); sheets.Add("sheet2", cnn.ExecuteReader("select 2 id")); MiniExcel.SaveAs("Demo.xlsx", sheets); }
#### 4. جدول البيانات

  • غير موصى به، سيقوم بتحميل جميع البيانات في الذاكرة
  • يستخدم DataTable التسمية التوضيحية لاسم العمود أولاً، ثم يستخدم اسم العمود
csharp var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); var table = new DataTable(); { table.Columns.Add("Column1", typeof(string)); table.Columns.Add("Column2", typeof(decimal)); table.Rows.Add("MiniExcel", 1); table.Rows.Add("Github", 2); }

MiniExcel.SaveAs(path, table);

####  5. استعلام Dapper

شكرًا لـ @shaofing #552، يرجى استخدام CommandDefinition + CommandFlags.NoCache

csharp using (var connection = GetConnection(connectionString)) { var rows = connection.Query( new CommandDefinition( @"select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2" , flags: CommandFlags.NoCache) ); // Note: QueryAsync will throw close connection exception MiniExcel.SaveAs(path, rows); }

الكود أدناه سيحمّل جميع البيانات في الذاكرة

csharp using (var connection = GetConnection(connectionString)) { var rows = connection.Query(@"select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2"); MiniExcel.SaveAs(path, rows); }
#### 6. الحفظ كـ MemoryStream  [[جرّب ذلك]](https://dotnetfiddle.net/JOen0e)

csharp using (var stream = new MemoryStream()) //support FileStream,MemoryStream ect. { stream.SaveAs(values); }
على سبيل المثال: واجهة برمجة تطبيقات لتصدير إكسل

csharp public IActionResult DownloadExcel() { var values = new[] { new { Column1 = "MiniExcel", Column2 = 1 }, new { Column1 = "Github", Column2 = 2} };

var memoryStream = new MemoryStream(); memoryStream.SaveAs(values); memoryStream.Seek(0, SeekOrigin.Begin); return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = "demo.xlsx" }; }

#### 7. إنشاء أوراق عمل متعددة

csharp // 1. Dictionary var users = new[] { new { Name = "Jack", Age = 25 }, new { Name = "Mike", Age = 44 } }; var department = new[] { new { ID = "01", Name = "HR" }, new { ID = "02", Name = "IT" } }; var sheets = new Dictionary { ["users"] = users, ["department"] = department }; MiniExcel.SaveAs(path, sheets);

// 2. DataSet var sheets = new DataSet(); sheets.Add(UsersDataTable); sheets.Add(DepartmentDataTable); //.. MiniExcel.SaveAs(path, sheets);

image

#### 8. خيارات TableStyles

النمط الافتراضي

image

بدون ضبط النمط

csharp var config = new OpenXmlConfiguration() { TableStyles = TableStyles.None }; MiniExcel.SaveAs(path, value,configuration:config);
image

#### 9. التصفية التلقائية (AutoFilter)

منذ الإصدار v0.19.0 يمكن لـ OpenXmlConfiguration.AutoFilter تفعيل أو تعطيل التصفية التلقائية، والقيمة الافتراضية هي true، وطريقة إعداد التصفية التلقائية تكون كالتالي:

csharp MiniExcel.SaveAs(path, value, configuration: new OpenXmlConfiguration() { AutoFilter = false });

#### 10. إنشاء صورة

csharp var value = new[] { new { Name="github",Image=File.ReadAllBytes(PathHelper.GetFile("images/github_logo.png"))}, new { Name="google",Image=File.ReadAllBytes(PathHelper.GetFile("images/google_logo.png"))}, new { Name="microsoft",Image=File.ReadAllBytes(PathHelper.GetFile("images/microsoft_logo.png"))}, new { Name="reddit",Image=File.ReadAllBytes(PathHelper.GetFile("images/reddit_logo.png"))}, new { Name="statck_overflow",Image=File.ReadAllBytes(PathHelper.GetFile("images/statck_overflow_logo.png"))}, }; MiniExcel.SaveAs(path, value);
image

#### 11. تصدير ملف مصفوفة البايتات

منذ الإصدار 1.22.0، عندما يكون نوع القيمة هو byte[] يقوم النظام بحفظ مسار الملف في الخلية بشكل افتراضي، وعند الاستيراد يمكن تحويلها إلى byte[]. وإذا لم ترغب في استخدام ذلك، يمكنك تعيين OpenXmlConfiguration.EnableConvertByteArray إلى false، مما يمكن أن يحسن من كفاءة النظام.

image

منذ الإصدار 1.22.0، عندما يكون نوع القيمة هو byte[] يقوم النظام بحفظ مسار الملف في الخلية بشكل افتراضي، وعند الاستيراد يمكن تحويلها إلى byte[]. وإذا لم ترغب في استخدام ذلك، يمكنك تعيين OpenXmlConfiguration.EnableConvertByteArray إلى false، مما يمكن أن يحسن من كفاءة النظام.

image

#### 12. دمج الخلايا المتشابهة عموديًا

هذه الوظيفة مدعومة فقط في تنسيق xlsx وتقوم بدمج الخلايا عموديًا بين وسوم @merge و @endmerge. يمكنك استخدام @mergelimit لتحديد حدود دمج الخلايا عموديًا.

csharp var mergedFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");

var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";

MiniExcel.MergeSameCells(mergedFilePath, path);

csharp var memoryStream = new MemoryStream();

var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";

memoryStream.MergeSameCells(path);

محتوى الملف قبل وبعد الدمج:

بدون حد للدمج:

Screenshot 2023-08-07 at 11 59 24

Screenshot 2023-08-07 at 11 59 57

مع حد للدمج:

Screenshot 2023-08-08 at 18 21 00

Screenshot 2023-08-08 at 18 21 40

#### 13. تخطي القيم الفارغة

خيار صريح جديد لكتابة خلايا فارغة للقيم null:

csharp DataTable dt = new DataTable();

/ ... /

DataRow dr = dt.NewRow();

dr["Name1"] = "Somebody once"; dr["Name2"] = null; dr["Name3"] = "told me.";

dt.Rows.Add(dr);

OpenXmlConfiguration configuration = new OpenXmlConfiguration() { EnableWriteNullValueCell = true // Default value. };

MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);


image
xml Somebody once told me.
السلوك السابق:

csharp / ... /

OpenXmlConfiguration configuration = new OpenXmlConfiguration() { EnableWriteNullValueCell = false // Default value is true. };

MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);


image
xml Somebody once told me.
يعمل مع القيم null و DBNull.

#### 14. تجميد الأعمدة والصفوف

csharp / ... /

OpenXmlConfiguration configuration = new OpenXmlConfiguration() { FreezeRowCount = 1, // default is 1 FreezeColumnCount = 2 // default is 0 };

MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);

image

تعبئة البيانات في قالب إكسل

  • التصريح مشابه لقالب Vue {{اسم المتغير}}، أو عرض مجموعة البيانات {{اسم المجموعة.اسم الحقل}}
  • عرض المجموعة يدعم IEnumerable/DataTable/DapperRow
#### 1. تعبئة أساسية

القالب: image

النتيجة: image

الكود:

csharp // 1. By POCO var value = new { Name = "Jack", CreateDate = new DateTime(2021, 01, 01), VIP = true, Points = 123 }; MiniExcel.SaveAsByTemplate(path, templatePath, value);

// 2. By Dictionary var value = new Dictionary() { ["Name"] = "Jack", ["CreateDate"] = new DateTime(2021, 01, 01), ["VIP"] = true, ["Points"] = 123 }; MiniExcel.SaveAsByTemplate(path, templatePath, value);

#### 2. تعبئة بيانات IEnumerable

ملاحظة1: استخدم أول IEnumerable من نفس العمود كأساس لتعبئة القائمة

القالب: image

النتيجة: image

الكود:

csharp //1. By POCO var value = new { employees = new[] { new {name="Jack",department="HR"}, new {name="Lisa",department="HR"}, new {name="John",department="HR"}, new {name="Mike",department="IT"}, new {name="Neo",department="IT"}, new {name="Loan",department="IT"} } }; MiniExcel.SaveAsByTemplate(path, templatePath, value);

//2. By Dictionary var value = new Dictionary() { ["employees"] = new[] { new {name="Jack",department="HR"}, new {name="Lisa",department="HR"}, new {name="John",department="HR"}, new {name="Mike",department="IT"}, new {name="Neo",department="IT"}, new {name="Loan",department="IT"} } }; MiniExcel.SaveAsByTemplate(path, templatePath, value);

#### 3. تعبئة البيانات المعقدة

ملاحظة: يدعم جداول متعددة واستخدام نفس المتغير

القالب:

image

النتيجة:

image

csharp // 1. By POCO var value = new { title = "FooCompany", managers = new[] { new {name="Jack",department="HR"}, new {name="Loan",department="IT"} }, employees = new[] { new {name="Wade",department="HR"}, new {name="Felix",department="HR"}, new {name="Eric",department="IT"}, new {name="Keaton",department="IT"} } }; MiniExcel.SaveAsByTemplate(path, templatePath, value);

// 2. By Dictionary var value = new Dictionary() { ["title"] = "FooCompany", ["managers"] = new[] { new {name="Jack",department="HR"}, new {name="Loan",department="IT"} }, ["employees"] = new[] { new {name="Wade",department="HR"}, new {name="Felix",department="HR"}, new {name="Eric",department="IT"}, new {name="Keaton",department="IT"} } }; MiniExcel.SaveAsByTemplate(path, templatePath, value);

#### 4. تعبئة أداء البيانات الضخمة

ملاحظة: استخدام IEnumerable مع التنفيذ المؤجل بدلاً من ToList يمكن أن يوفر أقصى قدر من استهلاك الذاكرة في MiniExcel

image

#### 5. نوع التعيين التلقائي لقيمة الخلية

القالب

image

النتيجة

image

الفئة

csharp public class Poco { public string @string { get; set; } public int? @int { get; set; } public decimal? @decimal { get; set; } public double? @double { get; set; } public DateTime? datetime { get; set; } public bool? @bool { get; set; } public Guid? Guid { get; set; } }
رمز

csharp var poco = new TestIEnumerableTypePoco { @string = "string", @int = 123, @decimal = decimal.Parse("123.45"), @double = (double)123.33, @datetime = new DateTime(2021, 4, 1), @bool = true, @Guid = Guid.NewGuid() }; var value = new { Ts = new[] { poco, new TestIEnumerableTypePoco{}, null, poco } }; MiniExcel.SaveAsByTemplate(path, templatePath, value);
#### 6. مثال :  قائمة مشاريع Github

النموذج

صورة

النتيجة

صورة

الكود

csharp var projects = new[] { new {Name = "MiniExcel",Link="https://github.com/mini-software/MiniExcel",Star=146, CreateTime=new DateTime(2021,03,01)}, new {Name = "HtmlTableHelper",Link="https://github.com/mini-software/HtmlTableHelper",Star=16, CreateTime=new DateTime(2020,02,01)}, new {Name = "PocoClassGenerator",Link="https://github.com/mini-software/PocoClassGenerator",Star=16, CreateTime=new DateTime(2019,03,17)} }; var value = new { User = "ITWeiHan", Projects = projects, TotalStar = projects.Sum(s => s.Star) }; MiniExcel.SaveAsByTemplate(path, templatePath, value);
#### 7. تعبئة البيانات المجمعة

csharp var value = new Dictionary() { ["employees"] = new[] { new {name="Jack",department="HR"}, new {name="Jack",department="HR"}, new {name="John",department="HR"}, new {name="John",department="IT"}, new {name="Neo",department="IT"}, new {name="Loan",department="IT"} } }; await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value);
##### 1. مع وسم @group ومع وسم @header

قبل

before_with_header

بعد

after_with_header

##### 2. مع وسم @group وبدون وسم @header

قبل

before_without_header

بعد

after_without_header

##### 3. بدون وسم @group

قبل

without_group

بعد

without_group_after

#### 8. عبارات If/ElseIf/Else داخل الخلية

القواعد:

  • يدعم DateTime, Double, Int مع العوامل ==, !=, >, >=, <, <=.
  • يدعم String مع العوامل ==, !=.
  • يجب أن تكون كل عبارة في سطر جديد.
  • يجب إضافة مسافة واحدة قبل وبعد العوامل.
  • لا يجب أن يكون هناك سطر جديد داخل العبارات.
  • يجب أن تكون الخلية بنفس التنسيق الموضح أدناه.
csharp @if(name == Jack) {{employees.name}} @elseif(name == Neo) Test {{employees.name}} @else {{employees.department}} @endif
قبل

if_before

بعد

if_after

#### 9. DataTable كمعامل

csharp var managers = new DataTable(); { managers.Columns.Add("name"); managers.Columns.Add("department"); managers.Rows.Add("Jack", "HR"); managers.Rows.Add("Loan", "IT"); } var value = new Dictionary() { ["title"] = "FooCompany", ["managers"] = managers, }; MiniExcel.SaveAsByTemplate(path, templatePath, value);
#### 10. الصيغ

##### 1. مثال قم بإضافة بادئة $ للمعادلة الخاصة بك واستخدم $enumrowstart و $enumrowend لتحديد مراجع بداية ونهاية الصفوف القابلة للتعداد:

image

عند عرض القالب، سيتم إزالة البادئة $ وسيتم استبدال $enumrowstart و $enumrowend بأرقام الصفوف البداية والنهاية للقابل للتعداد:

image

##### 2. أمثلة أخرى للصيغ:

| | | |--------------|-------------------------------------------------------------------------------------------| | الجمع | $=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) | | متوسط بديل | $=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) / COUNT(C{{$enumrowstart}}:C{{$enumrowend}}) | | النطاق | $=MAX(C{{$enumrowstart}}:C{{$enumrowend}}) - MIN(C{{$enumrowstart}}:C{{$enumrowend}}) |

#### 11. أخرى

##### 1. التحقق من مفتاح معلمة القالب

منذ الإصدار V1.24.0، يتم افتراضياً تجاهل مفتاح معلمة القالب المفقود واستبداله بسلسلة فارغة، ويمكن لـ IgnoreTemplateParameterMissing التحكم في رمي الاستثناء أم لا.

csharp var config = new OpenXmlConfiguration() { IgnoreTemplateParameterMissing = false, }; MiniExcel.SaveAsByTemplate(path, templatePath, value, config)
image

اسم عمود إكسل / الفهرس / تجاهل السمة

#### 1. تحديد اسم العمود، فهرس العمود، تجاهل العمود

مثال إكسل

image

الكود

csharp public class ExcelAttributeDemo { [ExcelColumnName("Column1")] public string Test1 { get; set; } [ExcelColumnName("Column2")] public string Test2 { get; set; } [ExcelIgnore] public string Test3 { get; set; } [ExcelColumnIndex("I")] // system will convert "I" to 8 index public string Test4 { get; set; } public string Test5 { get; } //wihout set will ignore public string Test6 { get; private set; } //un-public set will ignore [ExcelColumnIndex(3)] // start with 0 public string Test7 { get; set; } }

var rows = MiniExcel.Query(path).ToList(); Assert.Equal("Column1", rows[0].Test1); Assert.Equal("Column2", rows[0].Test2); Assert.Null(rows[0].Test3); Assert.Equal("Test7", rows[0].Test4); Assert.Null(rows[0].Test5); Assert.Null(rows[0].Test6); Assert.Equal("Test4", rows[0].Test7);

#### 2. التنسيق المخصص (ExcelFormatAttribute)

منذ الإصدار V0.21.0 يدعم الفئة التي تحتوي على طريقة ToString(string content) للتنسيق

الفئة

csharp public class Dto { public string Name { get; set; }

[ExcelFormat("MMMM dd, yyyy")] public DateTime InDate { get; set; } }

رمز

csharp var value = new Dto[] { new Issue241Dto{ Name="Jack",InDate=new DateTime(2021,01,04)}, new Issue241Dto{ Name="Henry",InDate=new DateTime(2020,04,05)}, }; MiniExcel.SaveAs(path, value);
النتيجة

image

الاستعلام يدعم تحويل التنسيق المخصص

image

#### 3. تعيين عرض العمود (ExcelColumnWidthAttribute)

csharp public class Dto { [ExcelColumnWidth(20)] public int ID { get; set; } [ExcelColumnWidth(15.50)] public string Name { get; set; } }
#### 4. تعيين أسماء أعمدة متعددة لنفس الخاصية.

csharp public class Dto { [ExcelColumnName(excelColumnName:"EmployeeNo",aliases:new[] { "EmpNo","No" })] public string Empno { get; set; } public string Name { get; set; } }

#### 5. System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttribute

منذ الإصدار 1.24.0، يدعم النظام System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttribute

C# public class TestIssueI4TXGTDto { public int ID { get; set; } public string Name { get; set; } [DisplayName("Specification")] public string Spc { get; set; } [DisplayName("Unit Price")] public decimal Up { get; set; } }
#### 6. خاصية عمود إكسل (ExcelColumnAttribute)

منذ الإصدار V1.26.0، يمكن تبسيط عدة خصائص كما يلي:

csharp public class TestIssueI4ZYUUDto { [ExcelColumn(Name = "ID",Index =0)] public string MyProperty { get; set; } [ExcelColumn(Name = "CreateDate", Index = 1,Format ="yyyy-MM",Width =100)] public DateTime MyProperty2 { get; set; } }
#### 7. خاصية DynamicColumnAttribute

منذ الإصدار V1.26.0، يمكننا تعيين خصائص العمود بشكل ديناميكي

csharp var config = new OpenXmlConfiguration { DynamicColumns = new DynamicExcelColumn[] { new DynamicExcelColumn("id"){Ignore=true}, new DynamicExcelColumn("name"){Index=1,Width=10}, new DynamicExcelColumn("createdate"){Index=0,Format="yyyy-MM-dd",Width=15}, new DynamicExcelColumn("point"){Index=2,Name="Account Point"}, } }; var path = PathHelper.GetTempPath(); var value = new[] { new { id = 1, name = "Jack", createdate = new DateTime(2022, 04, 12) ,point = 123.456} }; MiniExcel.SaveAs(path, value, configuration: config);
image

#### 8. خاصية الورقة الديناميكية

منذ الإصدار V1.31.4 يمكننا تعيين خصائص الورقة بشكل ديناميكي. يمكننا تعيين اسم الورقة وحالتها (مدى ظهورها).

csharp var configuration = new OpenXmlConfiguration { DynamicSheets = new DynamicExcelSheet[] { new DynamicExcelSheet("usersSheet") { Name = "Users", State = SheetState.Visible }, new DynamicExcelSheet("departmentSheet") { Name = "Departments", State = SheetState.Hidden } } };

var users = new[] { new { Name = "Jack", Age = 25 }, new { Name = "Mike", Age = 44 } }; var department = new[] { new { ID = "01", Name = "HR" }, new { ID = "02", Name = "IT" } }; var sheets = new Dictionary { ["usersSheet"] = users, ["departmentSheet"] = department };

var path = PathHelper.GetTempPath(); MiniExcel.SaveAs(path, sheets, configuration: configuration);

يمكننا أيضًا استخدام السمة الجديدة ExcelSheetAttribute:

C# [ExcelSheet(Name = "Departments", State = SheetState.Hidden)] private class DepartmentDto { [ExcelColumn(Name = "ID",Index = 0)] public string ID { get; set; } [ExcelColumn(Name = "Name",Index = 1)] public string Name { get; set; } }
### إضافة، حذف، تحديث

#### إضافة

الإصدار v1.28.0 يدعم إدراج بيانات CSV لعدد N من الصفوف بعد الصف الأخير

csharp // Origin { var value = new[] { new { ID=1,Name ="Jack",InDate=new DateTime(2021,01,03)}, new { ID=2,Name ="Henry",InDate=new DateTime(2020,05,03)}, }; MiniExcel.SaveAs(path, value); } // Insert 1 rows after last { var value = new { ID=3,Name = "Mike", InDate = new DateTime(2021, 04, 23) }; MiniExcel.Insert(path, value); } // Insert N rows after last { var value = new[] { new { ID=4,Name ="Frank",InDate=new DateTime(2021,06,07)}, new { ID=5,Name ="Gloria",InDate=new DateTime(2022,05,03)}, }; MiniExcel.Insert(path, value); }
image

الإصدار v1.37.0 يدعم إدراج ورقة جديدة في مصنف إكسل موجود

csharp // Origin excel { var value = new[] { new { ID=1,Name ="Jack",InDate=new DateTime(2021,01,03)}, new { ID=2,Name ="Henry",InDate=new DateTime(2020,05,03)}, }; MiniExcel.SaveAs(path, value, sheetName: "Sheet1"); } // Insert a new sheet { var value = new { ID=3,Name = "Mike", InDate = new DateTime(2021, 04, 23) }; MiniExcel.Insert(path, table, sheetName: "Sheet2"); }
#### حذف(انتظار)

#### تحديث(انتظار)

التحقق التلقائي من نوع Excel

  • سيتحقق MiniExcel مما إذا كان الملف xlsx أو csv بناءً على امتداد الملف بشكل افتراضي، ولكن قد يكون هناك عدم دقة، يرجى تحديده يدويًا.
  • لا يمكن معرفة نوع ملف Excel من الـ Stream، يرجى تحديده يدويًا.
csharp stream.SaveAs(excelType:ExcelType.CSV); //or stream.SaveAs(excelType:ExcelType.XLSX); //or stream.Query(excelType:ExcelType.CSV); //or stream.Query(excelType:ExcelType.XLSX);

CSV

#### ملاحظة

  • النوع الافتراضي للعودة هو string، ولن يتم تحويل القيمة إلى أرقام أو تاريخ/وقت، ما لم يتم تعريف النوع عبر التعميم القوي.
#### فاصل مخصص

الإعداد الافتراضي هو , كفاصل، يمكنك تعديل خاصية Seperator للتخصيص

csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { Seperator=';' }; MiniExcel.SaveAs(path, values,configuration: config);
منذ الإصدار V1.30.1 تم دعم وظيفة لتخصيص الفاصل (شكرًا لـ @hyzx86)

csharp var config = new CsvConfiguration() { SplitFn = (row) => Regex.Split(row, $"\"" target="_blank" rel="noopener noreferrer">\t,$)") .Select(s => Regex.Replace(s.Replace("\"\"", "\""), "^\"|\"$", "")).ToArray() }; var rows = MiniExcel.Query(path, configuration: config).ToList();
#### فاصل الأسطر المخصص

الإعداد الافتراضي هو \r\n كرمز للسطر الجديد، يمكنك تعديل خاصية NewLine للتخصيص

csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { NewLine='\n' }; MiniExcel.SaveAs(path, values,configuration: config);
#### الترميز المخصص

  • الترميز الافتراضي هو "كشف الترميز من علامات ترتيب البايتات" (detectEncodingFromByteOrderMarks: true)
  • إذا كانت لديك متطلبات ترميز مخصصة، يرجى تعديل خاصية StreamReaderFunc / StreamWriterFunc
csharp // Read var config = new MiniExcelLibs.Csv.CsvConfiguration() { StreamReaderFunc = (stream) => new StreamReader(stream,Encoding.GetEncoding("gb2312")) }; var rows = MiniExcel.Query(path, true,excelType:ExcelType.CSV,configuration: config);

// Write var config = new MiniExcelLibs.Csv.CsvConfiguration() { StreamWriterFunc = (stream) => new StreamWriter(stream, Encoding.GetEncoding("gb2312")) }; MiniExcel.SaveAs(path, value,excelType:ExcelType.CSV, configuration: config);

#### قراءة السلسلة الفارغة كقيمة فارغة (null)

بشكل افتراضي، يتم تعيين القيم الفارغة إلى string.Empty. يمكنك تعديل هذا السلوك

csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { ReadEmptyStringAsNull = true };
### DataReader

#### 1. GetReader منذ الإصدار 1.23.0، يمكنك استخدام GetDataReader

csharp using (var reader = MiniExcel.GetReader(path,true)) { while (reader.Read()) { for (int i = 0; i < reader.FieldCount; i++) { var value = reader.GetValue(i); } } }

غير متزامن

  • الإصدار v0.17.0 يدعم التشغيل غير المتزامن (شكرًا isdaniel ( SHIH,BING-SIOU)](https://github.com/isdaniel))
csharp public static Task SaveAsAsync(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null) public static Task SaveAsAsync(this Stream stream, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) public static Task> QueryAsync(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) public static Task> QueryAsync(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new() public static Task> QueryAsync(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new() public static Task>> QueryAsync(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) public static Task SaveAsByTemplateAsync(this Stream stream, string templatePath, object value) public static Task SaveAsByTemplateAsync(this Stream stream, byte[] templateBytes, object value) public static Task SaveAsByTemplateAsync(string path, string templatePath, object value) public static Task SaveAsByTemplateAsync(string path, byte[] templateBytes, object value) public static Task QueryAsDataTableAsync(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
- يدعم v1.25.0 الخيار cancellationToken.

أخرى

#### 1. التعداد (Enum)

تأكد من أن اسم العمود في إكسل واسم الخاصية متطابقان، سيقوم النظام بالربط التلقائي بينهما (غير حساس لحالة الأحرف)

image

منذ الإصدار V0.18.0 يدعم وصف التعداد (Enum Description)

csharp public class Dto { public string Name { get; set; } public I49RYZUserType UserType { get; set; } }

public enum Type { [Description("General User")] V1, [Description("General Administrator")] V2, [Description("Super Administrator")] V3 }

image

منذ الإصدار 1.30.0 أصبح يدعم التحويل من وصف Excel إلى Enum، شكرًا لـ @KaneLeung

#### 2. تحويل CSV إلى XLSX أو تحويل XLSX إلى CSV

csharp MiniExcel.ConvertXlsxToCsv(xlsxPath, csvPath); MiniExcel.ConvertXlsxToCsv(xlsxStream, csvStream); MiniExcel.ConvertCsvToXlsx(csvPath, xlsxPath); MiniExcel.ConvertCsvToXlsx(csvStream, xlsxStream);
`csharp
using (var excelStream = new FileStream(path: filePath, FileMode.Open, FileAccess.Read))
using (var csvStream = new MemoryStream())
{
   MiniExcel.ConvertXlsxToCsv(excelStream, csvStream);
}
#### 3. معلومات الثقافة المخصصة

منذ الإصدار 1.22.0، يمكنك تخصيص CultureInfo كما هو موضح أدناه، الافتراضي للنظام هو CultureInfo.InvariantCulture.

var config = new CsvConfiguration()
{
    Culture = new CultureInfo("fr-FR"),
};
MiniExcel.SaveAs(path, value, configuration: config);

// or MiniExcel.Query(path, configuration: config);

#### 4. حجم المخزن المؤقت المخصص

    public abstract class Configuration : IConfiguration
    {
        public int BufferSize { get; set; } = 1024 * 512;
    }
#### 5. وضع السرعة

لن يتحكم النظام في الذاكرة، لكن يمكنك الحصول على سرعة حفظ أعلى.

var config = new OpenXmlConfiguration() { FastMode = true };
MiniExcel.SaveAs(path, reader,configuration:config);
#### 6. إضافة صورة دفعة واحدة (MiniExcel.AddPicture)

يرجى إضافة الصور قبل إنشاء بيانات الصفوف دفعة واحدة، وإلا سيستهلك النظام ذاكرة كبيرة عند استدعاء AddPicture.

var images = new[]
{
    new MiniExcelPicture
    {
        ImageBytes = File.ReadAllBytes(PathHelper.GetFile("images/github_logo.png")),
        SheetName = null, // default null is first sheet
        CellAddress = "C3", // required
    },
    new MiniExcelPicture
    {
        ImageBytes = File.ReadAllBytes(PathHelper.GetFile("images/google_logo.png")),
        PictureType = "image/png", // default PictureType = image/png
        SheetName = "Demo",
        CellAddress = "C9", // required
        WidthPx = 100,
        HeightPx = 100,
    },
};
MiniExcel.AddPicture(path, images);
صورة

#### 7. الحصول على أبعاد الأوراق

var dim = MiniExcel.GetSheetDimensions(path);

أمثلة:

#### 1. إدراج SQL لملف كبير الحجم باستخدام SQLite & Dapper لتجنب نفاد الذاكرة (OOM)

ملاحظة: يرجى عدم استدعاء طرائق ToList/ToArray بعد الاستعلام Query، لأنها ستقوم بتحميل جميع البيانات في الذاكرة

using (var connection = new SQLiteConnection(connectionString))
{
    connection.Open();
    using (var transaction = connection.BeginTransaction())
    using (var stream = File.OpenRead(path))
    {
       var rows = stream.Query();
       foreach (var row in rows)
             connection.Execute("insert into T (A,B) values (@A,@B)", new { row.A, row.B }, transaction: transaction);
       transaction.Commit();
    }
}
الأداء: صورة

#### 2. عرض توضيحي لواجهة برمجة التطبيقات لتحميل/تنزيل ملفات Excel Xlsx باستخدام ASP.NET Core 3.1 أو MVC 5 جربها

public class ApiController : Controller
{
    public IActionResult Index()
    {
        return new ContentResult
        {
            ContentType = "text/html",
            StatusCode = (int)HttpStatusCode.OK,
            Content = @"
DownloadExcel
DownloadExcelFromTemplatePath
DownloadExcelFromTemplateBytes

Upload Excel


public IActionResult DownloadExcel() { var values = new[] { new { Column1 = "MiniExcel", Column2 = 1 }, new { Column1 = "Github", Column2 = 2} }; var memoryStream = new MemoryStream(); memoryStream.SaveAs(values); memoryStream.Seek(0, SeekOrigin.Begin); return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = "demo.xlsx" }; }

public IActionResult DownloadExcelFromTemplatePath() { string templatePath = "TestTemplateComplex.xlsx";

Dictionary value = new Dictionary() { ["title"] = "FooCompany", ["managers"] = new[] { new {name="Jack",department="HR"}, new {name="Loan",department="IT"} }, ["employees"] = new[] { new {name="Wade",department="HR"}, new {name="Felix",department="HR"}, new {name="Eric",department="IT"}, new {name="Keaton",department="IT"} } };

MemoryStream memoryStream = new MemoryStream(); memoryStream.SaveAsByTemplate(templatePath, value); memoryStream.Seek(0, SeekOrigin.Begin); return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = "demo.xlsx" }; }

private static Dictionary TemplateBytesCache = new Dictionary();

static ApiController() { string templatePath = "TestTemplateComplex.xlsx"; byte[] bytes = System.IO.File.ReadAllBytes(templatePath); TemplateBytesCache.Add(templatePath, bytes); }

public IActionResult DownloadExcelFromTemplateBytes() { byte[] bytes = TemplateBytesCache["TestTemplateComplex.xlsx"];

Dictionary value = new Dictionary() { ["title"] = "FooCompany", ["managers"] = new[] { new {name="Jack",department="HR"}, new {name="Loan",department="IT"} }, ["employees"] = new[] { new {name="Wade",department="HR"}, new {name="Felix",department="HR"}, new {name="Eric",department="IT"}, new {name="Keaton",department="IT"} } };

MemoryStream memoryStream = new MemoryStream(); memoryStream.SaveAsByTemplate(bytes, value); memoryStream.Seek(0, SeekOrigin.Begin); return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = "demo.xlsx" }; }

public IActionResult UploadExcel(IFormFile excel) { var stream = new MemoryStream(); excel.CopyTo(stream);

foreach (var item in stream.Query(true)) { // do your logic etc. }

return Ok("File uploaded successfully"); } }

#### 3. استعلام التصفح (Paging Query)

void Main()
{
    var rows = MiniExcel.Query(path);

Console.WriteLine("==== No.1 Page ===="); Console.WriteLine(Page(rows,pageSize:3,page:1)); Console.WriteLine("==== No.50 Page ===="); Console.WriteLine(Page(rows,pageSize:3,page:50)); Console.WriteLine("==== No.5000 Page ===="); Console.WriteLine(Page(rows,pageSize:3,page:5000)); }

public static IEnumerable Page(IEnumerable en, int pageSize, int page) { return en.Skip(page * pageSize).Take(pageSize); }

20210419

#### 4. تصدير Excel من WebForm باستخدام memorystream

var fileName = "Demo.xlsx";
var sheetName = "Sheet1";
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
response.AddHeader("Content-Disposition", $"attachment;filename=\"{fileName}\"");
var values = new[] {
    new { Column1 = "MiniExcel", Column2 = 1 },
    new { Column1 = "Github", Column2 = 2}
};
var memoryStream = new MemoryStream();
memoryStream.SaveAs(values, sheetName: sheetName);
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(Response.OutputStream);
response.End();

#### 5. إدارة ديناميكية للدعم متعدد اللغات (i18n) وصلاحيات الأدوار

على غرار المثال، أنشئ طريقة للتعامل مع إدارة i18n والصلاحيات، واستخدم yield return لإرجاع IEnumerable> لتحقيق معالجة ديناميكية وفعالة من حيث الذاكرة

void Main()
{
    var value = new Order[] {
        new Order(){OrderNo = "SO01",CustomerID="C001",ProductID="P001",Qty=100,Amt=500},
        new Order(){OrderNo = "SO02",CustomerID="C002",ProductID="P002",Qty=300,Amt=400},
    };

Console.WriteLine("en-Us and Sales role"); { var path = Path.GetTempPath() + Guid.NewGuid() + ".xlsx"; var lang = "en-US"; var role = "Sales"; MiniExcel.SaveAs(path, GetOrders(lang, role, value)); MiniExcel.Query(path, true).Dump(); }

Console.WriteLine("zh-CN and PMC role"); { var path = Path.GetTempPath() + Guid.NewGuid() + ".xlsx"; var lang = "zh-CN"; var role = "PMC"; MiniExcel.SaveAs(path, GetOrders(lang, role, value)); MiniExcel.Query(path, true).Dump(); } }

private IEnumerable> GetOrders(string lang, string role, Order[] orders) { foreach (var order in orders) { var newOrder = new Dictionary();

if (lang == "zh-CN") { newOrder.Add("客户编号", order.CustomerID); newOrder.Add("订单编号", order.OrderNo); newOrder.Add("产品编号", order.ProductID); newOrder.Add("数量", order.Qty); if (role == "Sales") newOrder.Add("价格", order.Amt); yield return newOrder; } else if (lang == "en-US") { newOrder.Add("Customer ID", order.CustomerID); newOrder.Add("Order No", order.OrderNo); newOrder.Add("Product ID", order.ProductID); newOrder.Add("Quantity", order.Qty); if (role == "Sales") newOrder.Add("Amount", order.Amt); yield return newOrder; } else { throw new InvalidDataException($"lang {lang} wrong"); } } }

public class Order { public string OrderNo { get; set; } public string CustomerID { get; set; } public decimal Qty { get; set; } public string ProductID { get; set; } public decimal Amt { get; set; } }

image

الأسئلة الشائعة

#### س: عنوان رأس Excel لا يساوي اسم خاصية الصنف، كيف يمكن عمل التعيين؟

ج. يرجى استخدام السمة ExcelColumnName

image

#### س: كيف يمكن الاستعلام أو التصدير لعدة أوراق؟

ج. استخدم طريقة GetSheetNames مع معلمة sheetName في الاستعلام.

var sheets = MiniExcel.GetSheetNames(path);
foreach (var sheet in sheets)
{
    Console.WriteLine($"sheet name : {sheet} ");
    var rows = MiniExcel.Query(path,useHeaderRow:true,sheetName:sheet);
    Console.WriteLine(rows);
}
image

#### س. كيف يمكن الاستعلام أو تصدير معلومات حول ظهور الورقة؟

ج. طريقة GetSheetInformations.

var sheets = MiniExcel.GetSheetInformations(path);
foreach (var sheetInfo in sheets)
{
    Console.WriteLine($"sheet index : {sheetInfo.Index} "); // next sheet index - numbered from 0
    Console.WriteLine($"sheet name : {sheetInfo.Name} ");   // sheet name
    Console.WriteLine($"sheet state : {sheetInfo.State} "); // sheet visibility state - visible / hidden
}
#### س. هل استخدام Count سيؤدي إلى تحميل جميع البيانات في الذاكرة؟

لا، اختبار الصورة يحتوي على مليون صف * 10 أعمدة من البيانات، وأقصى استخدام للذاكرة هو أقل من 60 ميجابايت، ويستغرق 13.65 ثانية

image

#### س. كيف يستخدم الاستعلام الفهارس الرقمية؟

الفهرس الافتراضي في الاستعلام هو مفتاح نصي: A,B,C.... إذا كنت تريد تغييره إلى فهرس رقمي، يرجى إنشاء الطريقة التالية للتحويل

void Main()
{
    var path = @"D:\git\MiniExcel\samples\xlsx\TestTypeMapping.xlsx";
    var rows = MiniExcel.Query(path,true);
    foreach (var r in ConvertToIntIndexRows(rows))
    {
        Console.Write($"column 0 : {r[0]} ,column 1 : {r[1]}");
        Console.WriteLine();
    }
}

private IEnumerable> ConvertToIntIndexRows(IEnumerable rows) { ICollection keys = null; var isFirst = true; foreach (IDictionary r in rows) { if(isFirst) { keys = r.Keys; isFirst = false; }

var dic = new Dictionary(); var index = 0; foreach (var key in keys) dic[index++] = r[key]; yield return dic; } } #### س. لماذا يتم إنشاء ملف Excel فارغ بدون عنوان عندما تكون القيمة فارغة عند التصدير إلى Excel؟

لأن MiniExcel يستخدم منطقاً مشابهاً لـ JSON.NET للحصول على النوع ديناميكياً من القيم لتبسيط عمليات الـ API، ولا يمكن معرفة النوع بدون بيانات. يمكنك مراجعة المشكلة رقم 133 لمزيد من الفهم.

image

الأنواع القوية وDataTable ستُولّد رؤوس أعمدة، لكن Dictionary ستظل تولد Excel فارغ

#### س. كيف يمكن إيقاف foreach عند الصف الفارغ؟

يمكن استخدام MiniExcel مع LINQ TakeWhile لإيقاف حلقة foreach التكرارية.

Image

#### س. كيف يمكن حذف الصفوف الفارغة؟

image

IEnumerable :

public static IEnumerable QueryWithoutEmptyRow(Stream stream, bool useHeaderRow, string sheetName, ExcelType excelType, string startCell, IConfiguration configuration)
{
    var rows = stream.Query(useHeaderRow,sheetName,excelType,startCell,configuration);
    foreach (IDictionary row in rows)
    {
        if(row.Keys.Any(key=>row[key]!=null))
            yield return row;
    }
}

جدول البيانات :

public static DataTable QueryAsDataTableWithoutEmptyRow(Stream stream, bool useHeaderRow, string sheetName, ExcelType excelType, string startCell, IConfiguration configuration)
{
    if (sheetName == null && excelType != ExcelType.CSV) /Issue #279/
        sheetName = stream.GetSheetNames().First();

var dt = new DataTable(sheetName); var first = true; var rows = stream.Query(useHeaderRow,sheetName,excelType,startCell,configuration); foreach (IDictionary row in rows) { if (first) {

foreach (var key in row.Keys) { var column = new DataColumn(key, typeof(object)) { Caption = key }; dt.Columns.Add(column); }

dt.BeginLoadData(); first = false; }

var newRow = dt.NewRow(); var isNull=true; foreach (var key in row.Keys) { var _v = row[key]; if(_v!=null) isNull = false; newRow[key] = _v; }

if(!isNull) dt.Rows.Add(newRow); }

dt.EndLoadData(); return dt; }

#### س. كيف يمكن استخدام SaveAs(path,value) لاستبدال الملف الموجود دون ظهور خطأ "الملف ...xlsx موجود بالفعل"

يرجى استخدام فئة Stream لتخصيص منطق إنشاء الملف، على سبيل المثال:

`C# using (var stream = File.Create("Demo.xlsx")) MiniExcel.SaveAs(stream,value);

أو، منذ الإصدار V1.25.0، تدعم SaveAs معامل overwriteFile لتمكين/تعطيل الكتابة فوق الملف الموجود

csharp MiniExcel.SaveAs(path, value, overwriteFile: true); ``

القيود والمحاذير

  • لا يتم دعم ملفات xls والملفات المشفرة حالياً
  • ملفات xlsm تدعم فقط الاستعلام

المرجع

ExcelDataReader / ClosedXML / Dapper / ExcelNumberFormat

الشكر

#### Jetbrains

jetbrains-variant-2

شكرًا لتوفير رخصة مجانية لجميع منتجات IDE لهذا المشروع (الرخصة)

مشاركة المساهمة والتبرع

الرابط https://github.com/orgs/mini-software/discussions/754

المساهمون

--- Tranlated By Open Ai Tx | Last indexed: 2025-07-17 ---