需要有效扫描大型 excel 文件中的低端 ascii 控制字符
Need to efficiently scans for low end ascii control characters on large excel files
我正在研究 ETL 验证程序,以在处理之前扫描无效的 ascii 字符。在这种情况下,无效被定义为 0-31 范围内的 ascii 字符。
在 C# ETL 验证服务中,我使用 OfficeOpenXml 检查 excel 文件的内容。
有谁知道除了循环每个工作表、每一列和每一行之外还有更有效的搜索内容的方法吗?文件可能非常大,验证应该尽可能快。
是否可以访问原始 xml 缓冲区?解压缩 xml 文件并扫描那里的内容会更快吗?
首先,我认为是时候进行 Speed Rant 了:https://ericlippert.com/2012/12/17/performance-rant/
问题是,现在的瓶颈在哪里。我的教唆者告诉我 应该 是磁盘。您正在处理文件,因此它通常是磁盘。如果是这样的话,除了将每个单元格只加载一次内存之外,没有什么可以加速的。
然而,您正在对字符串进行相当深入的处理,因为您必须遍历每个字符。因此可能需要 相关 的时间花在这上面。很可能不是瓶颈,而是可以作为成本否定的东西。
您可以进行一些异步处理,在您处理这个的同时让下一个 cell/row 在后台加载。 Directory.EnumerateFiles()
vs Directory.GetFiles
这样的方法可能有效:https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.enumeratefiles
Row 似乎具有 GetEnumerator 功能。但它可能只是为 需要 Enuemrators 而实际上不包括 defered/background 加载的代码获取枚举器(即像为 foreach 循环隐式创建的枚举器)。
我编写了测试工具并使用 string[] 和数据结构列表以及以下迭代器提出了 14 种变体:
1 :
foreach (char c in s.ToCharArray())
2 :
byte[] ASCIIValues = Encoding.ASCII.GetBytes(s);
foreach (byte code in ASCIIValues)
3 :
Regex rx = new Regex(@"/[^ -~]/", RegexOptions.Compiled | RegexOptions.IgnoreCase);
MatchCollection matches = rx.Matches(s);
4 :
for(int x=0; x < s.Length; x++)
- 使用方法 2 对字符串[] 进行 foreach 迭代的项目少于 100,000 个时,平均时间最快。
- 超过 100,000 个项目在一个字符串上使用 Parallel.ForEach 使用方法 2 平均最快。
- 超过 1,000,000 个项目使用 Parallel.ForEach 字符串 [] 使用方法 1 平均最快。
- 在 string[] 或 List<> 上使用 Parallel.ForEach 使用所有方法的 10,000 个项目是最慢的。
- 在没有并行的情况下,1 超过 2 是最快的,因为项目数超过了 500,000(??)
- 与迭代字符或字节相比,正则表达式总是更慢
- 对于大约 100,000 个使用字符串 [] 和方法 2 的 foreach 循环是最快的
我正在研究 ETL 验证程序,以在处理之前扫描无效的 ascii 字符。在这种情况下,无效被定义为 0-31 范围内的 ascii 字符。
在 C# ETL 验证服务中,我使用 OfficeOpenXml 检查 excel 文件的内容。
有谁知道除了循环每个工作表、每一列和每一行之外还有更有效的搜索内容的方法吗?文件可能非常大,验证应该尽可能快。
是否可以访问原始 xml 缓冲区?解压缩 xml 文件并扫描那里的内容会更快吗?
首先,我认为是时候进行 Speed Rant 了:https://ericlippert.com/2012/12/17/performance-rant/
问题是,现在的瓶颈在哪里。我的教唆者告诉我 应该 是磁盘。您正在处理文件,因此它通常是磁盘。如果是这样的话,除了将每个单元格只加载一次内存之外,没有什么可以加速的。
然而,您正在对字符串进行相当深入的处理,因为您必须遍历每个字符。因此可能需要 相关 的时间花在这上面。很可能不是瓶颈,而是可以作为成本否定的东西。
您可以进行一些异步处理,在您处理这个的同时让下一个 cell/row 在后台加载。 Directory.EnumerateFiles()
vs Directory.GetFiles
这样的方法可能有效:https://docs.microsoft.com/en-us/dotnet/api/system.io.directory.enumeratefiles
Row 似乎具有 GetEnumerator 功能。但它可能只是为 需要 Enuemrators 而实际上不包括 defered/background 加载的代码获取枚举器(即像为 foreach 循环隐式创建的枚举器)。
我编写了测试工具并使用 string[] 和数据结构列表以及以下迭代器提出了 14 种变体:
1 :
foreach (char c in s.ToCharArray())
2 :
byte[] ASCIIValues = Encoding.ASCII.GetBytes(s);
foreach (byte code in ASCIIValues)
3 :
Regex rx = new Regex(@"/[^ -~]/", RegexOptions.Compiled | RegexOptions.IgnoreCase);
MatchCollection matches = rx.Matches(s);
4 :
for(int x=0; x < s.Length; x++)
- 使用方法 2 对字符串[] 进行 foreach 迭代的项目少于 100,000 个时,平均时间最快。
- 超过 100,000 个项目在一个字符串上使用 Parallel.ForEach 使用方法 2 平均最快。
- 超过 1,000,000 个项目使用 Parallel.ForEach 字符串 [] 使用方法 1 平均最快。
- 在 string[] 或 List<> 上使用 Parallel.ForEach 使用所有方法的 10,000 个项目是最慢的。
- 在没有并行的情况下,1 超过 2 是最快的,因为项目数超过了 500,000(??)
- 与迭代字符或字节相比,正则表达式总是更慢
- 对于大约 100,000 个使用字符串 [] 和方法 2 的 foreach 循环是最快的