【问题标题】:Parsing a textfile in C# with skipping some contents在 C# 中解析文本文件并跳过一些内容
【发布时间】:2009-05-06 19:17:51
【问题描述】:

我正在尝试解析具有标题和正文的文本文件。在该文件的标题中,有对正文部分的行号引用。例如:

SECTION_A 256
SECTION_B 344
SECTION_C 556

这意味着 SECTION_A 从第 256 行开始。

将这个标题解析成字典然后在必要时阅读这些部分的最佳方法是什么。

典型的场景是:

  1. 解析标题和只读部分 SECTION_B
  2. 解析标题并阅读每个部分的第一段。

数据文件很大,我绝对不想全部加载到内存中再操作。

非常感谢您的建议。我的环境是 VS 2008 和 C# 3.5 SP1。

【问题讨论】:

    标签: c# .net file-io text-parsing


    【解决方案1】:

    你可以很容易地做到这一点。

    这个问题分为三个部分。

    1) 如何查找文件中一行的开始位置。做到这一点的唯一方法是从文件中读取行,保留一个记录该行在文件中的开始位置的列表。例如

    列表 lineMap = new List(); lineMap.Add(0); // 第 0 行从数据文件中的位置 0 开始(只是一个虚拟条目) lineMap.Add(0); // 第 1 行从数据文件中的位置 0 开始 使用 (StreamReader sr = new StreamReader("DataFile.txt")) { 字符串线; int lineNumber = 1; 而 ((line = sr.ReadLine()) != null) lineMap.Add(sr.BaseStream.Position); }

    2) 读取索引文件并将其解析为字典。

    字典索引 = new Dictionary(); 使用 (StreamReader sr = new StreamReader("IndexFile.txt")) { 字符串线; 而 ((line = sr.ReadLine()) != null) { string[] 部分 = line.Split(' '); // 将行分成名称和行号 index.Add(parts[0], Convert.ToInt32(parts[1])); } }

    然后要在文件中查找一行,请使用:

    int lineNumber = index["SECTION_B";]; // 将节名转换为行号 长 offsetInDataFile = lineMap[lineNumber]; // 将行号转换为文件偏移量

    然后在 DataFile.txt 上打开一个新的 FileStream,Seek(offsetInDataFile, SeekOrigin.Begin) 移动到行首,并使用 StreamReader(如上)从中读取行。

    【讨论】:

      【解决方案2】:

      嗯,显然您可以将名称 + 行号存储到字典中,但这对您没有任何好处。

      好吧,当然,它可以让您知道从哪一行开始读取,但问题是,该行在文件中的什么位置?唯一知道的方法就是从头开始,开始数数。

      最好的方法是编写一个包装器来解码文本内容(如果你有编码问题)并且可以给你一个行号到字节位置类型的映射,那么你可以取那个行号 256,然后看在字典中知道第 256 行从文件中的位置 10000 开始,然后从那里开始读取。

      这是一次性处理的情况吗?如果没有,您是否考虑过将整个文件填充到本地数据库中,例如 SQLite 数据库?这将允许您在行号与其内容之间进行直接映射。当然,那个文件会比你原来的文件还要大,而且你需要将数据从文本文件复制到数据库,所以这两种方式都有一些开销。

      【讨论】:

      • 谢谢,我担心在开始操作之前必须对文件进行预处理。外部数据库对我来说不是一个选项,因为数据文件为此经常更改,无论如何,谢谢您的回答
      • 你可以偷懒,只做从开始到你感兴趣的部分的预处理。从 0 到那个点的每个访问都将是 .Seek():able 并且如果你对后面的部分感兴趣,您可以从您离开的地方继续进行预处理。还存储带有时间戳和任何可识别信息的索引以供以后使用(或在数据更新时将其彻底删除)。
      【解决方案3】:

      只需一次读取一行文件并忽略数据,直到找到所需的数据。您不会有任何内存问题,但性能可能不会很好。不过,您可以在后台线程中轻松完成此操作。

      【讨论】:

      • 按行号对标题中定义的所需部分进行排序可能是个好主意,然后按该顺序读取它们,这样只需要通过一次。
      【解决方案4】:

      读取文件直到标题结束,假设你知道那在哪里。拆分您存储在空格中的字符串,如下所示:

      Dictionary<string, int> sectionIndex = new Dictionary<string, int>();
      List<string> headers = new List<string>(); // fill these with readline
      
      foreach(string header in headers) {
          var s = header.Split(new[]{' '});
          sectionIndex.Add(s[0], Int32.Parse(s[1]));
      }
      

      找到您想要的字典条目,计算文件中读取的行数,然后循环直到您到达该行号,然后读取直到到达下一节的起始行。我不知道你是否可以保证字典中键的顺序,所以你可能需要当前和下一个部分的名称。

      请务必进行一些错误检查,以确保您正在阅读的部分不在您正在阅读的部分之前,以及您能想到的任何其他错误情况。

      【讨论】:

        【解决方案5】:

        您可以逐行阅读,直到所有标题信息都被捕获并停止(假设所有部分指针都在标题中)。您将获得用于稍后检索数据的节和行号。

        string dataRow = "";
        
        try
        {
            TextReader tr = new StreamReader("filename.txt");
        
            while (true)
            {
                dataRow = tr.ReadLine();
                if (dataRow.Substring(1, 8) != "SECTION_")
                    break;
                else
                    //Parse line for section code and line number and log values
                    continue;
            }
            tr.Close();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-15
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多