【问题标题】:How do I read a specified line in a text file?如何读取文本文件中的指定行?
【发布时间】:2010-11-18 18:38:49
【问题描述】:

给定一个文本文件,我将如何读取任意行而文件中没有其他内容?

说,我有一个文件 test.txt。我将如何读取文件中的第 15 行?

我所看到的只是将整个文本文件存储为字符串数组,然后使用行号的值作为要从数组中使用的字符串的编号......但有一些复杂性:文本文件非常庞大,而我正在编写的应用程序的机器并不完全是一流的系统。速度不是重中之重,但绝对是一个主要问题。

有没有办法读取文本文件的特定行并将结果存储为字符串?

感谢您的回复: 该文件是有点结构化的。它有 25 行信息,然后是 X 行数字,但前 25 行中的第 17 行的值为 X

但是,有 1 个空白行,它作为文件中的第二条记录重复自身,并且 X 可以为每条记录具有不同的值。

我想要做的是读取前 25 行并将其存储为独立值,然后将接下来的 X 行(通常大约 250 行)存储为数组。然后我将它存储在 SQL 数据库中,并重复 NEXT 记录,直到我到达第 Y 条记录(文件中的记录数在第 3 行)

编辑 2: 好的,我想我已经根据大家的回答得出了一个解决方案。

我将读取前 25 行并将其存储为数组。我会将数组的相关内容复制到局部变量,然后删除前 25 行。然后,我可以使用该信息将接下来的 X 行(数组中第 13 项的值)存储为数组,对其进行序列化,将其存储在数据库中,然后删除我刚刚读取的行.

然后我可以为每个后续记录重复该过程。

当然,这取决于我所做的一个假设,老实说,我不确定这是真的。是否可以在 C# 中从文本文件中删除前 n 行,而无需阅读整个内容并在没有前 n 行的情况下重新编写它? p>

【问题讨论】:

  • 行本身是否固定长度(以字节为单位)。这对可用的解决方案类型产生了很大的影响
  • “它有 25 行信息”:我认为这与下面提到的固定行假设相矛盾,您只需阅读所有行并沿途获取您想要的内容。
  • 你真正需要的不是直接获取第i行的系统。您只需要一次性处理文件并在其中执行一些操作。您可以使用我的答案(ReadLine 方法)中的代码 sn-p 一次读取一行。
  • 谢谢大家,我想我找到了解决方案 * if * 我可以有效地执行前 n 行。
  • 我不知道你为什么需要删除这些行?你不能继续阅读你需要的下一行吗?

标签: c# string file-io input


【解决方案1】:

.NET 4.0 编辑

从 .NET 4.0 开始,可以直接访问文件的单行。例如,访问第 15 行:

string line = File.ReadLines(FileName).Skip(14).Take(1).First();

这将只返回所需的行


由于您无法预测文件中第 i 行的位置(可以吗?),您也必须阅读所有前面的行。如果行号较小,这可能比ReadAllLines 方法更有效。

string GetLine(string fileName, int line)
{
   using (var sr = new StreamReader(fileName)) {
       for (int i = 1; i < line; i++)
          sr.ReadLine();
       return sr.ReadLine();
   }
}

【讨论】:

  • 除非您的文本文件具有固定的行长。
  • Mehrdad,你能解释一下吗?使用 UTF-8 读取第 1234 行?
  • @Mehrdad:非常正确 - 无需查看文件中的任何数据即可预测。
  • @Merhdad:UTF16 不是固定长度的,但它的前身 UCS-2 是(我最近才知道这个区别)。见en.wikipedia.org/wiki/UTF-16/UCS-2
  • 旁注:如果你只想读一行ElementAt(OrDefault)更合适:string line = File.ReadLines(FileName).ElementAtOrDefault(14);
【解决方案2】:

如果每一行都是固定长度,那么您可以围绕它打开一个 Stream,在文件中查找(每行字节数)* n 并从那里读取您的行。

using( Stream stream = File.Open(fileName, FileMode.Open) )
{
    stream.Seek(bytesPerLine * (myLine - 1), SeekOrigin.Begin);
    using( StreamReader reader = new StreamReader(stream) )
    {
        string line = reader.ReadLine();
    }
}

或者,您可以只使用 StreamReader 读取行,直到找到您想要的行。这种方式速度较慢,但​​仍然比阅读每一行有所改进。

using( Stream stream = File.Open(fileName, FileMode.Open) )
{
    using( StreamReader reader = new StreamReader(fileStream) )
    {
        string line = null;
        for( int i = 0; i < myLineNumber; ++i )
        {
            line = reader.ReadLine();
        }
    }
}

【讨论】:

    【解决方案3】:

    不,不幸的是没有。在原始级别的文件不能在行号的基础上工作。相反,它们以位置/偏移量为基础工作。根文件系统没有行的概念。这是更高级别组件添加的概念。

    所以没有办法告诉操作系统,请在 blah 行打开文件。相反,您必须打开文件并跳过计算新行,直到您通过了指定的数字。然后将下一组字节存储到一个数组中,直到您点击下一个新行。

    【讨论】:

      【解决方案4】:

      除非您有固定大小的行,否则您需要阅读每一行,直到到达您想要的行。虽然,您不需要存储每一行​​,但如果它不是您想要的行,则将其丢弃。

      编辑:

      如前所述,如果行长是可预测的,也可以在文件中查找 - 也就是说,您可以应用一些确定性函数将行号转换为文件位置。

      【讨论】:

      • +1,但就像 Jon Skeet 所说,您需要以字节为单位的固定大小,这意味着 ASCII 编码。
      • Henk:固定字节怎么就意味着 ASCII?
      • 确实:UTF-32 会浮现在脑海,甚至是 ISO-8859-1 之类的东西。
      • 使用 UTF-8 转义序列用于特殊字符,即Ă 比 A 占用(几个)更多字节。
      • 好的,你需要一个固定大小的编码(ASCII、UTF-32、ISO-8859-1、..)和一个固定的行长度。
      【解决方案5】:

      正如 Mehrdad 所说,您不能只查找第 n 行而不读取文件。 但是,您不需要将整个文件存储在内存中 - 只需丢弃不需要的数据即可。

      string line;
      
      using (var sr = new StreamReader(path))
          for (int i = 0; i < 15; i++)
          {
             line = sr.ReadLine();
             if (line == null) 
                 break; // there are less than 15 lines in the file
          }
      

      【讨论】:

        【解决方案6】:

        如果行的长度都是固定的,您可以使用流的 Seek 方法移动到正确的起始位置。

        如果行的长度可变,则您的选择会受到更多限制。

        如果这是一个您只会使用一次然后丢弃的文件,那么您最好阅读它并在内存中使用它。

        如果这是一个您将保留的文件并且将读取多于写入的文件,您可以创建一个包含每行起始位置的自定义索引文件。然后使用该索引来获取您的搜索位置。创建索引文件的过程是资源密集型的。每次向文件中添加新行时,都需要更新索引,因此维护成为一个不小的问题。

        【讨论】:

          【解决方案7】:

          如果您的文件包含不同长度的行,并且您需要经常读取行并且需要快速读取它,您可以通过读取一次来创建文件索引,保存每个新行的位置,然后在需要时读取一行,您只需在索引中查找该行的位置,在那里查找然后读取该行。

          如果您在文件中添加新行,您可以只添加新行的索引,而无需全部重新索引。虽然如果您的文件在您已经索引的某处发生更改,那么您必须重新索引。

          【讨论】:

            【解决方案8】:

            每次读五行,只要把你的语句放在 if 语句中就可以了

                    String str1 = @"C:\Users\TEMP\Desktop\StaN.txt";   
            
                    System.IO.StreamReader file = new System.IO.StreamReader(str1);
            
                    line = file.ReadLine();
            
                    Int32 ctn=0;
            
                    try
                    {
            
                        while ((line = file.ReadLine()) != null)
                        {
            
                                if (Counter == ctn)
                                {
                                    MessageBox.Show("I am here");
                                    ctn=ctn+5;
                                    continue;
                                }
                                else
                                {
                                    Counter++;
                                    //MessageBox.Show(Counter.ToString());
                                    MessageBox.Show(line.ToString());
                                } 
                            }
            
                        file.Close();
                    }
                    catch (Exception er)
                    {
            
                    }
            

            【讨论】:

              【解决方案9】:

              您可以逐行阅读,因此您不必一次阅读全部内容(可能根本没有)

              int i=0
              while(!stream.eof() && i!=lineNum)
                  stream.readLine()
                  i++
              line = stream.readLine()
              

              【讨论】:

              • 逐行阅读的问题是每次阅读都会有延迟和搜索。如果文件有很多行,则性能将贯穿整个楼层。读取大块数据(比如 64k 或更多)并在内存中查找换行符会有更好的性能。
              • RB Davidson:如果流被缓冲,那将不是问题。
              • 并非所有流都被缓冲。您可以强制缓冲流,但您的示例中没有任何内容暗示正在执行此操作。无论如何,我在使用缓冲流读取非常大的文件并逐行读取时遇到了性能问题。通过强制流读取比单行更大的数据块,然后在内存中拆分行,我能够显着提高性能。
              【解决方案10】:

              虽然您不能直接在非对称文件中查找第 N 行而不读取文件中的数据(因为您需要计算已进入文件的行数),但您可以计算换行符,直到您找到所需的内存最少且性能可能最好的行。

              这将比将所有内容读取到数组中更节省内存,因为它只会读入文件,直到它到达文件末尾或行号(以先到者为准)。它远非完美,但可能会满足您的需求:

              string line15 = ReadLine(@"C:\File.csv", 15);
              
              public string ReadLine(string FilePath, int LineNumber){
                  string result = "";
                  try{
                  if( File.Exists(FilePath) ){
                      using (StreamReader _StreamReader = new StreamReader(FilePath)){
                      for (int a = 0; a < LineNumber; a++) {
                          result = _StreamReader.ReadLine();
                      }
                      }
                  }
                  }catch{}
                  return result;
              }
              

              【讨论】:

                【解决方案11】:

                久经考验。就这么简单:

                string line = File.ReadLines(filePath).ElementAt(actualLineNumber - 1);
                

                只要你有一个文本文件,这应该可以工作。 稍后,根据您希望读取的数据,您可以相应地转换字符串并使用它。

                【讨论】:

                • 你应该提供一些解释,而不仅仅是一段应该盲目信任的代码。
                【解决方案12】:

                一种变体。如果行号大于行数,则产生错误。

                string GetLine(string fileName, int lineNum)
                {
                    using (StreamReader sr = new StreamReader(fileName))
                    {
                        string line;
                        int count = 1;
                        while ((line = sr.ReadLine()) != null)
                        {
                            if(count == lineNum)
                            {
                                return line;
                            }
                            count++;
                        }
                    }
                    return "line number is bigger than number of lines";  
                }
                

                【讨论】:

                  【解决方案13】:
                          if (File.Exists(fpath))
                          {
                  
                              var data = File.ReadLines(fpath);
                              Console.WriteLine(data.ToArray()[14]);
                          }
                  

                  【讨论】:

                    猜你喜欢
                    • 2014-02-13
                    • 1970-01-01
                    • 2016-03-26
                    • 2021-03-25
                    • 2017-07-16
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2012-10-27
                    相关资源
                    最近更新 更多