using System.Text; using MesETL.App.HostedServices.Abstractions; using Microsoft.Extensions.Logging; namespace MesETL.App.Services.ETL; /// /// CSV文件读取 /// public class CsvReader : IDataReader { protected readonly string? FilePath; protected readonly Lazy Reader; protected readonly ILogger? Logger; protected readonly string TableName; public DataRecord Current { get; protected set; } = null!; public string[] Headers { get; } public string Delimiter { get; } public char QuoteChar { get; } public CsvReader(Stream stream, string tableName, string[] headers, string delimiter = ",", char quoteChar = '"', ILogger? logger = null) : this(tableName, headers, delimiter, quoteChar, logger) { Reader = new Lazy(() => new StreamReader(stream)); } public CsvReader(string filePath, string tableName, string[] headers, string delimiter = ",", char quoteChar = '"', ILogger? logger = null) : this(tableName, headers, delimiter, quoteChar, logger) { var fs = File.OpenRead(filePath); FilePath = filePath; Reader = new Lazy(() => new StreamReader(fs)); } private CsvReader(string tableName, string[] headers, string delimiter = ",", char quoteChar = '"', ILogger? logger = null) { TableName = tableName; Headers = headers; Logger = logger; Delimiter = delimiter; QuoteChar = quoteChar; Reader = null!; } public virtual async ValueTask ReadAsync() { var str = await Reader.Value.ReadLineAsync(); if (string.IsNullOrWhiteSpace(str)) return false; var fields = ParseRow(str, QuoteChar, Delimiter); Current = new DataRecord(fields, TableName, Headers); return true; } public string[] ParseRow(ReadOnlySpan source, char quoteChar, string delimiter) { var result = new List(); var index = -1; var current = new StringBuilder(); var hasQuote = false; var hasSlash = false; while (index < source.Length - 1) { index++; if (hasSlash == false && source[index] == '\\') { hasSlash = true; current.Append('\\'); continue; } if (hasSlash == false && source[index] == quoteChar) { hasQuote = !hasQuote; current.Append(source[index]); continue; } if (hasQuote == false && source[index] == delimiter[0]) { result.Add(current.ToString()); current.Clear(); } else { current.Append(source[index]); } hasSlash = false; } result.Add(current.ToString()); return result.ToArray(); } public virtual void Dispose() { if(Reader.IsValueCreated) Reader.Value.Dispose(); } }