【问题标题】:Read text file at specific line [duplicate]在特定行读取文本文件[重复]
【发布时间】:2013-04-03 11:52:52
【问题描述】:

我有一个超过 3000 行的文本文件。我正在使用

查找行数
string[] lines = File.ReadAllLines(myPath);
var lineCount = lines.Length; 

然后我生成一个随机数

Random rand = new Random();
var lineToRead = rand.Next(1, lineCount);

现在我需要读取由随机数生成的特定行。我可以使用

string requiredLine = lines[lineToRead];

因为我的文件很大,所以我认为创建这么大的数组效率不高。有没有更有效或更简单的方法来做到这一点?

【问题讨论】:

  • 这是速度和内存使用之间的常见问题。您的方式是内存使用问题,逐行读取将是速度问题。当然,现在我更喜欢在内存中读取 3000 行
  • 您至少应该扫描您的文件以查找结束行字符。因此,您可以使用 ReadLine 进行 rand 计数,但不能一次获得正确的行。
  • @Steve:File.ReadAllLines() 的实现只是使用了对 StreamReader.ReadLine() 的重复调用,所以它不会比自己显式执行更快。
  • 我不明白为什么删除了两个答案。他们似乎在工作,但我再也看不到这些答案了。
  • @MatthewWatson 你是对的。我应该知道的更好。因此,在这种情况下,最好逐行查找所需的行。当然,如果 OP 使用不同的索引重复操作,则情况并非如此

标签: c#


【解决方案1】:

这是一个迭代文件两次的解决方案(第一次计算行数,下一次选择行)。好处是您不需要在内存中创建一个包含 3000 个字符串的数组。但是,如上所述,它可能会更慢。为什么可能? - 因为File.ReadAllLines 在其中创建了一个字符串列表,并且该列表将在填充 3000 个项目时多次调整大小。 (初始容量为4。当内部数组完全填满时,将创建一个双倍大小的新数组,并将所有字符串复制到那里)。

因此,该解决方案使用File.ReadLines 方法返回IEnumerable<string>,其中包含您不需要的行和跳过行:

IEnumerable<string> lines = File.ReadLines(myPath);
var lineToRead = rand.Next(1, lines.Count());
var line = lines.Skip(lineToRead - 1).First();

顺便说一句,内部File.ReadLines 使用SteamReader 逐行读取文件。

【讨论】:

  • @DominicKexel 实际上我将解决方案恢复到最初的解决方案,并在性能和内存使用方面添加了一些 cmets
  • 很高兴看到周围仍有人认真对待批评以改进他们的答案:-)
【解决方案2】:

您可以做的是解析文件以找到每行的索引,然后您可以稍后通过使用 Stream.Position 获取内容返回到某一行。使用这种方法,您不需要在内存中保留任何内容,而且速度相当快。 我在一个 20K 行和 1MB 大小的文件上对此进行了测试。索引文件需要 7 毫秒,获取行需要 0.3 毫秒。

    // Parse the file
    var indexes = new List<long>();
    using (var fs = File.OpenRead("text.txt"))
    {
        indexes.Add(fs.Position);
        int chr;
        while ((chr = fs.ReadByte()) != -1)
        {
            if (chr == '\n')
            {                        
                indexes.Add(fs.Position);
            }
        }
    }

    int lineCount = indexes.Count;
    int randLineNum = new Random().Next(0, lineCount - 1);
    string lineContent = "";


    // Read the random line
    using (var fs = File.OpenRead("text.txt"))
    {
        fs.Position = indexes[randLineNum];
        using (var sr = new StreamReader(fs))
        {
            lineContent = sr.ReadLine();
        }
    }

【讨论】:

    【解决方案3】:

    您可以将您的流包装到 StreamReader 中,并根据需要多次调用 ReadLine 以转到您的目标行。这样您就不需要将整个文件内容保存在内存中。

    但是,这只有在您很少这样做并且文件很大时才可行。

    【讨论】:

    • 那么你怎么知道你要调用多少次ReadLine
    • 要查找总行数,您可以使用相同的 StreamReader 并计算新行字符数从文件中一一读取所有字符。
    【解决方案4】:

    使用储层采样一次性解决此问题

    如果您想从预先不知道该列表长度的项目列表中随机选择一个或多个项目,您可以使用Reservoir Sampling

    我们可以利用这一点,连同File.ReadLines() 方法(它避免缓冲内存中的所有行)来编写一个单遍算法,该算法将只读取每行一次,而无需缓冲。

    下面的示例代码显示了一个通用解决方案,可让您随机选择任意数量的行。对于您的情况,N = 1。

    示例代码还包括一个测试程序,以证明行是随机选择的且分布均匀。

    (要了解此代码的工作原理,请参阅我上面链接的 Wiki 文章。)

    using System;
    using System.IO;
    using System.Collections.Generic;
    
    namespace Demo
    {
        internal class Program
        {
            public static List<string> RandomlyChooseLinesFromFile(string filename, int n, Random rng)
            {
                var result = new List<string>(n);
                int index = 0;
    
                foreach (var line in File.ReadLines(filename))
                {
                    if (index < n)
                    {
                        result.Add(line);
                    }
                    else
                    {
                        int r = rng.Next(0, index + 1);
    
                        if (r < n)
                            result[r] = line;
                    }
    
                    ++index;
                }
    
                return result;
            }
    
            // Test RandomlyChooseLinesFromFile()
    
            private static void Main(string[] args)
            {
                Directory.CreateDirectory("C:\\TEST");
                string testfile = "C:\\TEST\\TESTFILE.TXT";
                File.WriteAllText(testfile, "0\n1\n2\n3\n4\n5\n6\n7\n8\n9");
                var rng = new Random();
                int trials = 100000;
                var counts = new int[10];
    
                for (int i = 0; i < trials; ++i)
                {
                    string line = RandomlyChooseLinesFromFile(testfile, 1, rng)[0];
                    int index = int.Parse(line);
                    ++counts[index];
                }
    
                // If this algorithm is correct, each line should be chosen
                // approximately 10% of the times.
    
                Console.WriteLine("% times each line was chosen:\n");
    
                for (int i = 0; i < 10; ++i)
                {
                    Console.WriteLine("{0} = {1}%", i, 100*counts[i]/(double)trials);
                }
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      下面将帮助您阅读文件中的特定行..

      http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/4dbd68f6-61f5-4d36-bfa0-5c909101874b

      代码片段

      using System;
      using System.Collections.Generic;
      using System.Text;
      using System.IO;
      
      namespace ReadLine
      {
      class Program
      {
          static void Main(string[] args)
          {
              //Load our text file
              TextReader tr = new StreamReader("\\test.txt");
      
              //How many lines should be loaded?
              int NumberOfLines = 15;
      
              //Make our array for each line
              string[] ListLines = new string[NumberOfLines];
      
              //Read the number of lines and put them in the array
              for (int i = 1; i < NumberOfLines; i++)
              {
                  ListLines[i] = tr.ReadLine();
              }
      
              //This will write the 5th line into the console
              Console.WriteLine(ListLines[5]);
              //This will write the 1st line into the console
              Console.WriteLine(ListLines[1]);
      
              Console.ReadLine();
      
              // close the stream
              tr.Close();
          }
      }
      }
      

      这些也很有帮助..

      http://www.tek-tips.com/viewthread.cfm?qid=1460456

      How do I read a specified line in a text file?

      下面是编辑用的

      Edit a specific Line of a Text File in C#

      希望对你有帮助...

      【讨论】:

      • 其实这就是他在做的事情,或多或少,他不想使用巨大的数组
      • 是的。我更深入地研究了这个问题,但找不到更好的解决方案。但我认为,如果没有数组,还有其他可用的解决方案会增加复杂性。如果我在某处错了,请纠正我......
      • 那么这如何帮助从文件中获取随机行?您发布的代码不是读取所有行,而是仅读取 x 行...
      • 发布的代码是一个snipet..这样他可以得到一个更好的主意..他可以自己添加随机生成代码..我认为C#不支持指针,否则我已经发布了如果它是 C 或 C++ 中的,我自己的带有指针的代码..
      【解决方案6】:

      你可以像下面这样尝试......它不能创建任何大数组但得到一个特定的行......

      string path = "D:\\Software.txt";
      int lines = File.ReadAllLines(path).Length;
      Random rand = new Random();
      var lineToRead = rand.Next(1, lines);
      var requiredLine = System.IO.File.ReadLines(path).Skip(lineToRead - 1).First();
      Console.WriteLine(requiredLine.ToString());
      

      【讨论】:

      • 这实际上将整个文件加载到一个大数组的内存中。这就是File.ReadAllLines 所做的。
      • @DominicKexel:我想我只是通过使用 File.ReadAllLines 找到了一个长度。我没有将这些行存储在任何地方。所以内存仍然占用......?
      • 是的,该方法仍然加载整个文件,无论是否将结果存储在变量中。您正在访问您创建的数组的 Length 属性。为此,数组必须在那里,不是吗?您可以改用File.ReadLines(myPath).Count(),它不会将整个文件内容存储在一个数组中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多