【问题标题】:Read last line of text file读取文本文件的最后一行
【发布时间】:2012-07-22 11:02:22
【问题描述】:

我需要知道如何读取文本文件的最后一行。我需要找到该行,然后将其处理到 SQL 数据库中……我一直在阅读并在网上搜索,但我正在努力寻找正确的方法来做到这一点。即:

  1. 查找文件的最后一行。
  2. 处理文件的最后一行。

我希望这是有道理的。

【问题讨论】:

  • 你试过什么?请注意,我们只能为“1”提供帮助……“2”完全取决于您。
  • 字节 b; fs.Seek(0, SeekOrigin.End); for (long offset = 0; offset
  • @DebbieDippenaar 对于非常大的文件,可能不是一个坏主意。就我个人而言,我可能会一次读取 buffers 字节,并且您会遇到多字节编码的一些主要问题,但是...

标签: c#


【解决方案1】:

有两种方法:简单而低效,或者极其复杂但高效。复杂的版本假定一个理智的编码。

除非您的文件如此太大以至于您真的无法阅读全部内容,否则我只会使用:

var lastLine = File.ReadLines("file.txt").Last();

注意这里使用的是File.ReadLines不是File.ReadAllLines。如果您使用 .NET 3.5 或更早版本,则需要使用 File.ReadAllLines 或编写您自己的代码 - ReadAllLines 将一次性将 整个 文件读入内存,而 ReadLines流式传输。

否则,复杂的方法是使用类似于this的代码。它尝试从文件末尾向后读取,处理诸如 UTF-8 多字节字符之类的脏乱。这不愉快。

【讨论】:

  • 如果你知道文件的编码,如果你只是在寻找后面有其他字符的最后一个换行符,你真的不需要处理太多的麻烦。例如。 U+000D U+000A 在任何可变长度编码(例如 UTF-8 或 UTF-16)中都不会是可变长度的。通常可以假设它后面的所有其他内容都是格式良好的。
  • @Joey:棘手的一点是,如果您向后搜索到文件中的任意位置,您需要注意您可能是中间字符。对于 UTF-16 来说不是什么大问题,但在 UTF-8 中,您需要将自己与字符边界对齐......并且您可以跳入“\r\n”的“\n”,所以你会有再次倒退。基本上,看看链接答案中的代码。如果你能写一个更简单的版本,请写,我很想看看。
  • 我一定在这里遗漏了一些东西。 File.ReadLines 返回一个 System.Collections.Generic.IEnumerable 对象,当我尝试使用它时它没有“Last”方法。我的代码针对 .NET 4.0 框架。我是否缺少一些程序集参考或其他内容?
  • @dscarr:您缺少using 指令:using System.Linq;
  • 注意对大文件使用此功能的性能影响。
【解决方案2】:

我会简单地将File.ReadLines(path)Enumerable.Last 结合起来:

String last = File.ReadLines(@"C:\file.txt").Last();

它流式传输这些行并且不会将所有行作为File.ReadAllLines 加载到内存中。

【讨论】:

  • 如何获取文本文件的最后一个符号??
  • @Neel 倒退到最后一行,但只有一个字符。
【解决方案3】:

第一部分:

File.ReadAllLines(@"c:\some\path\file.txt").Last();

File.ReadLines(@"c:\some\path\file.txt").Last();

ReadLines 是首选。

【讨论】:

    【解决方案4】:
    string m = "";
    StreamReader r = new StreamReader("file_path");
    while (r.EndOfStream == false)
    {
        m = r.ReadLine();
    }
    Console.WriteLine("{0}\n", m);
    r.Close();
    

    【讨论】:

      【解决方案5】:

      注意:所有这些代码都假定为 UTF-8。如果您需要支持使用双宽字符(如 Unicode)的代码页,则需要在换行符之前和/或之后对字符添加额外检查,以确保它确实是换行符。

      这个问题的主要用例之一是抓取日志文件的末尾。不幸的是,当日志文件达到兆字节时,其他答案就惨死了。想象一下,在一个微小的单核 VPS 上每次调用都运行每一行......哎呀。

      UTF-8 的好处在于,当您点击 '\n' 字符时,您不必担心任何相关字节,因为在 UTF8-8 中任何高位清除的字节都只是一个 ASCII 字符.很方便!

      您可以使用“How to read a text file reversely with iterator in C#”的解决方案,但请注意代码相当复杂。如果您只需要一个简单的 UTF-8 行尾,此解决方案将非常有效,即使在大型日志文件上也能表现出色。

      如果您同时监视大量文件并在 C# 中使用 FileSystemWatcher 之类的东西,那么这种性能提升将非常重要。我在廉价的单 CPU Linux VPS 上使用非常相似的代码来监控登录失败,并将 IP 地址放入我的 MIT 许可项目 https://github.com/DigitalRuby/IPBan 的防火墙中,使用 https://github.com/DigitalRuby/IPBan/blob/master/IPBanCore/Core/Utility/LogFileScanner.cs(一次处理多个新行)。

      当您的 SSH 端口面向公众时,您会惊讶于 auth.log 的大小。如果你经常阅读几十个甚至上百个文件,你会很庆幸没有使用File.ReadAllLines().Last();

      由于这只是一页代码,因此在简单和非常快速之间取得了很好的平衡。

      C# 代码 ...

      /// <summary>
      /// Utility class to read last line from a utf-8 text file in a performance sensitive way. The code does not handle a case where more than one line is written at once.
      /// </summary>
      public static class UTF8FileUtilities
      {
          /// <summary>
          /// Read the last line from the file. This method assumes that each write to the file will be terminated with a new line char ('\n')
          /// </summary>
          /// <param name="path">Path of the file to read</param>
          /// <returns>The last line or null if a line could not be read (empty file or partial line write in progress)</returns>
          /// <exception cref="Exception">Opening or reading from file fails</exception>
          public static string ReadLastLine(string path)
          {
              // open read only, we don't want any chance of writing data
              using (System.IO.Stream fs = System.IO.File.OpenRead(path))
              {
                  // check for empty file
                  if (fs.Length == 0)
                  {
                      return null;
                  }
      
                  // start at end of file
                  fs.Position = fs.Length - 1;
      
                  // the file must end with a '\n' char, if not a partial line write is in progress
                  int byteFromFile = fs.ReadByte();
                  if (byteFromFile != '\n')
                  {
                      // partial line write in progress, do not return the line yet
                      return null;
                  }
      
                  // move back to the new line byte - the loop will decrement position again to get to the byte before it
                  fs.Position--;
      
                  // while we have not yet reached start of file, read bytes backwards until '\n' byte is hit
                  while (fs.Position > 0)
                  {
                      fs.Position--;
                      byteFromFile = fs.ReadByte();
                      if (byteFromFile < 0)
                      {
                          // the only way this should happen is if someone truncates the file out from underneath us while we are reading backwards
                          throw new System.IO.IOException("Error reading from file at " + path);
                      }
                      else if (byteFromFile == '\n')
                      {
                          // we found the new line, break out, fs.Position is one after the '\n' char
                          break;
                      }
                      fs.Position--;
                  }
      
                  // fs.Position will be right after the '\n' char or position 0 if no '\n' char
                  byte[] bytes = new System.IO.BinaryReader(fs).ReadBytes((int)(fs.Length - fs.Position));
                  return System.Text.Encoding.UTF8.GetString(bytes);
              }
          }
      }
      

      【讨论】:

      • 我认为您错过了将文件的相关部分复制到您的 bytes 缓冲区。 stackoverflow.com/a/24412022/343340
      • 很好,补充。
      • "不管代码页如何,\n 最有可能总是代表一个换行符。"不过,这可能不是角色的全部。您假设之后的字节是最后一行的开头,但情况可能并非如此。例如,使用Encoding.Unicode,“\n”表示为0x0a,0x00。你最终会从角色的一半开始阅读。 (您还假设对 Stream.Read 的一次调用将在一次调用中读取文件的其余部分,这通常不是一个好的假设。)
      • 换句话说,你为了效率牺牲了正确性甚至不知道效率是否重要。在许多情况下,效率真的无关紧要,只要您事先知道您的标准是什么。正确性几乎总是很重要。
      • 当然不是。但读取文件最后一行的主要原因是经常这样做。在这种情况下,随着文件的增长,其他答案会越来越慢,不必要地占用 CPU 和磁盘。或者想象一下尾随 10 或 100 个文件。现在事情变得棘手,那些复制和粘贴最佳答案的人正在摸不着头脑,为什么他们的服务器陷入困境。性能并不总是最重要的,但重要的是要认识到这是一个性能危急情况。正如我所提到的,即使是我的廉价 Linux vps 也无法每秒从 1mb 文件中读取所有行。
      【解决方案6】:
      string last = File.ReadLines(@"C:\file.txt").Last();
      string lastsymbol = last[last.Count - 1];
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-10-15
        • 1970-01-01
        • 1970-01-01
        • 2020-09-27
        • 1970-01-01
        • 1970-01-01
        • 2023-04-07
        • 2013-03-07
        相关资源
        最近更新 更多