提问人:Arfaoui Arij 提问时间:5/9/2023 最后编辑:user190245Arfaoui Arij 更新时间:5/10/2023 访问量:208
如何解析包含多个串联表的 CSV 文件,每个表都有自己的标题行?
How can I parse a CSV file that contains multiple concatenated tables, each with their own header row?
问:
我正在开发一个桌面应用程序,该应用程序将接收多个文件并将其中的信息存储到 SQL 数据库中(基本上就像自动 ETL 过程一样)。
我在解析文件时发现的问题是,一个文件包含多个“表”或标题,而不是我在互联网上找到代码的文件(第一行通常是列名,其余是所有数据)。
有谁知道我如何解析它?
答:
0赞
MarkPflug
5/9/2023
#1
我维护一个可能适合您的库:Sylvan.Data.Csv。它支持包含“多个表”的 CSV 文件。要使此功能正常工作,它要求每个表具有不同数量的列,或者它们之间有一个空行。
以下示例演示如何使用“MutliResult”模式:
using Sylvan.Data.Csv;
var data =
"""
a,b,c
1,2,3
d,e,f,g
4,5,6,7
""";
// MutliResult will identify a new "table" any time the number of columns changes
// any empty lines between tables are skipped.
var opts = new CsvDataReaderOptions { ResultSetMode = ResultSetMode.MultiResult };
// CsvDataReader can be used as a DbDataReader, so can be fed directly to SqlBulkCopy.
System.Data.Common.DbDataReader csv = CsvDataReader.Create(new StringReader(data), opts);
do
{
Console.WriteLine(csv.GetName(0));
while (csv.Read())
{
Console.WriteLine(csv.GetString(0));
}
Console.WriteLine("---");
} while (csv.NextResult());
// outputs:
// a
// 1
// ---
// d
// 4
// ---
但是,如果两个连续的表包含相同数量的列,并且没有空行分隔它们,则此功能将不起作用,因为它会将后续表视为第一个表的延续。
在这种情况下,可以使用 Sylvan.Data 库的一项功能来确定下一个表的开始位置。
此示例使用“TakeWhile”扩展方法来标识下一个表的开始时间。
using Sylvan.Data;
using Sylvan.Data.Csv;
var data =
"""
a,b,c
1,2,3
d,e,f
4,5,6
""";
var csv = CsvDataReader.Create(new StringReader(data));
// the batchReader will read until it finds a row starting with "d".
// you can customize this logic to identify when the next table starts in your data.
// The batchReader here is a wrapper around the csv reader, and will yield rows as long as the "TakeWhile"
// predicate is true
System.Data.Common.DbDataReader batchReader = csv.TakeWhile(r => r.GetString(0) != "d");
// process the table using the standard DbDataReader APIs.
while (batchReader.Read())
{
Console.WriteLine(csv.GetName(0));
Console.WriteLine(batchReader.GetString(0));
}
Console.WriteLine("---");
// The csv reader is now positioned on the start of the next table
// calling initialize will re-initialize the CsvDataReader with the current row
// this will cause the "d,e,f" headers to be loaded.
csv.Initialize();
// consume the rest of the CSV data.
// Or, you might need to use another TakeWhile.
while (csv.Read())
{
Console.WriteLine(csv.GetName(0));
Console.WriteLine(csv.GetString(0));
}
Console.WriteLine("---");
第二个示例生成的输出与第一个示例相同。
评论
CsvHelper
Stream
FileStream
Stream
Regex
Stream
String
String