c# - 将大文本拆分为较小块的最快方法

标签 c# .net substring

我已经使用下面的代码来拆分字符串,但是需要很多时间。

using (StreamReader srSegmentData = new StreamReader(fileNamePath))
{
    string strSegmentData = "";
    string line = srSegmentData.ReadToEnd();
    int startPos = 0;

    ArrayList alSegments = new ArrayList();
    while (startPos < line.Length && (line.Length - startPos) >= segmentSize)
    {
        strSegmentData = strSegmentData + line.Substring(startPos, segmentSize) + Environment.NewLine;
        alSegments.Add(line.Substring(startPos, segmentSize) + Environment.NewLine);
        startPos = startPos + segmentSize;
    }
}

请给我建议一种将字符串拆分为固定大小的较小块的替代方法

最佳答案

首先,您应该定义块大小的含义。如果您的意思是具有固定数量 的块代码单元那么您的实际算法可能很慢,但它有效。如果这不是您想要的并且您实际上是指具有固定数量 的块字符 然后它坏了。我在这篇代码审查帖子中讨论了一个类似的问题:Split a string into chunks of the same length那么我将在这里只重复相关部分。

  • 您正在对 Char 进行分区但是 String是 UTF-16 编码的,那么您可能会在至少三种情况下产生损坏的字符串:
  • 一个字符用多个编码单元编码 .该字符的 Unicode 代码点被编码为两个 UTF-16 代码单元,每个代码单元可能会在两个不同的切片中结束(并且两个字符串都将无效)。
  • 一个字符由多个码位组成 .您正在处理由两个单独的 Unicode 代码点组成的字符(例如汉字 𠀑)。
  • 一个字符具有组合字符或修饰符 .这比您想象的更常见:例如,用于构建 à 的 Unicode 组合字符(如 U+0300 COMBINING GRAVE ACCENT)和 Unicode 修饰符(如 U+02BC MODIFIER LETTER APOSTROPHE)。
  • 编程语言和人类对字符的定义非常不同,例如在斯洛伐克语中 dž 是单个字符,但它由 2/3 Unicode 代码点组成,在这种情况下也是 2/3 UTF-16 代码单元"dž".Length > 1 .更多关于这个和其他文化问题的信息,请访问 How can I perform a Unicode aware character by character comparison? .
  • 存在连字。假设一个连字是一个代码点(并假设它被编码为一个代码单元),那么您会将其视为单个字形,但它代表两个字符。在这种情况下该怎么办?一般来说,字符的定义可能非常模糊,因为根据使用该词的学科,它具有不同的含义。您不能(可能)正确处理所有事情,但您应该设置一些约束和记录代码行为。

  • 一个提议的(和未经测试的)实现可能是这样的:
    public static IEnumerable<string> Split(this string value, int desiredLength)
    {
        var characters = StringInfo.GetTextElementEnumerator(value);
        while (characters.MoveNext())
            yield return String.Concat(Take(characters, desiredLength));
    }
    
    private static IEnumerable<string> Take(TextElementEnumerator enumerator, int count)
    {
        for (int i = 0; i < count; ++i)
        {
            yield return (string)enumerator.Current;
    
            if (!enumerator.MoveNext())
                yield break;
        }
    }
    

    它没有针对速度进行优化(如您所见,我尝试使用枚举使代码保持简短和清晰),但是对于大文件,它的性能仍然比您的实现好(原因请参见下一段)。

    关于您的代码,请注意:
  • 你正在 build 一个巨大的ArrayList (?!) 保存结果。另请注意,以这种方式调整大小 ArrayList多次(即使给定输入大小和块大小,则其最终大小是已知的)。
  • strSegmentData多次重建,如果需要积累字符必须使用StringBuilder否则每个操作都会分配一个新字符串并复制旧值(它很慢,而且还给垃圾收集器增加了压力)。

  • 有更快的实现(请参阅链接的代码审查帖子,尤其是 Heslacher's implementation 以获得更快的版本),如果您不需要正确处理 Unicode(您是 确定 您只管理美国 ASCII 字符)然后还有一个漂亮的readable implementation from Jon Skeet (请注意,在分析您的代码后,您仍然可以通过预先分配正确大小的输出列表来提高大文件的性能)。我不会在这里重复他们的代码,然后请引用链接的帖子。

    在您的具体 您不需要读取内存中的整个大文件 ,您可以一次读取/解析 n 个字符(不要太担心磁盘访问,I/O 是缓冲的)。它会稍微降低性能,但会大大提高内存使用率。或者,您可以逐行阅读(管理处理跨线块)。

    关于c# - 将大文本拆分为较小块的最快方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34430325/

    相关文章:

    regex - bash:除了单引号中的内容之外,我如何对所有内容进行子字符串化?

    c# - 哪种设计模式用于过滤查询? C#

    c# - 如何更改或替换数据表中的所有列名

    c# - linq中的左外连接

    .net - 通过远程桌面提高 WPF 应用程序速度?

    c# - 如何在字典中插入第一个元素?

    c# - 如何在 WinForms 中实现这种窗口样式?

    python - 从字符串中删除子字符串时出现意外结果

    javascript - 如何从字符串中删除文本?

    c# - PHP等价于C#的多维数组声明