读取文本文件最后一行之前的两行
Read two lines before last line of text file
我有一个日志文件,我正试图从中获取一些信息。我需要的信息在最后一行之前的那一行,最后一行 is/could 是空白的。所以如果最后一行是空白,它实际上是最后一行之前的一行或最后一行之前的两行。
我知道如何使用以下方法到达文件的最后一行:
var lastLine = File.ReadLines("SomeFile.log").Last();
我还可以使用 Linq 使用 .skipWhile() 或 .skip(1) 来跳过行,但不会向后移动。
我不确定如何到达我需要的线路。这是日志文件最后几行的示例(最后一行为空白):
2021/05/02 23:47:57:008989 send_status_message(2) Info: "Stream status heartbeat sent: [SY 1.3.2 ]"
2021/05/02 23:47:57:225172 send_status_message(2) Info: "Received heartbeat response: [S ]"
2021/05/03 00:00:00:045055 set_log_dir(2) Info: "Changing log directory to /abc/def/logs/2021-05-03."
<blank-line>
我正在尝试获取该行的时间戳(即 2021/05/02 23:47:57:225172)。
像这样的东西可能适合你
var lines = System.IO.File.ReadLines(@"SomeFile.log");
var secondLastIdx = lines.Count() - 2;
var secondlast = lines.Skip(secondLastIdx ).First();
您可能需要使用更好的方法来计算 secondLastIdx
使用 C#8 的 range operator
如果你的数组已经在内存中并且可以使用 C# 8,你可以这样做:
var Lines = File.ReadAllLines("SomeFile.log");
var SecondToLast = Lines[^2];
没有 C#8。
或者,如 Tim 所述,您可以在索引器上进行算术运算:
var Lines = File.ReadAllLines("SomeFile.log");
var SecondToLast = Lines[Lines.Length - 2];
基于评论的编辑。
从您的评论来看,您似乎不太确定会得到多少空行。如果是这种情况,您最好使用更通用的方法,例如:
static string FirstNotEmpty(string[] Lines, bool BottomUp = false)
{
if (BottomUp)
{
for (int i = Lines.Length - 1; i >= 0; i--)
{
var CurrentLine = Lines[i];
if (!string.IsNullOrWhiteSpace(CurrentLine))
return CurrentLine;
}
}
else
{
for (int i = 0; i <= Lines.Length-1; i++)
{
var CurrentLine = Lines[i];
if (!string.IsNullOrWhiteSpace(CurrentLine))
return CurrentLine;
}
}
return null; //Or something else.
}
在你的情况下,你会这样称呼它:
var FirstNotEmptyLine = FirstNotEmpty(Lines, BottomUp: true);
您也可以先发制人地从数组中删除空行:
var WithoutEmptyLines = Lines.Where(x => !string.IsNullOrWhiteSpace(x));
然后“安全地”获取最后一行。
也许你可以使用这个扩展方法:
public static class EnumerableExtensions
{
public static T GetLastItem<T>(this IEnumerable<T> seq, int countFromEnd)
{
if(seq is IList<T> list) return list[^countFromEnd];
using var enumerator = seq.Reverse().GetEnumerator();
while(enumerator.MoveNext())
{
if(--countFromEnd == 0) return enumerator.Current;
}
throw new ArgumentOutOfRangeException();
}
}
用法:
var secondLastLine = File.ReadLines("SomeFile.log").GetLastItem(2);
如果你不使用C#8,那么你不能使用Ranges,将return list[^countFromEnd]
替换为return list[list.Count - countFromEnd]
。
File.ReadLines("SomeFile.log").Last();
将遍历所有行并保留最后一行。这对于大文件来说可能很昂贵。至少它不会将它们全部保留在内存中。
更快的替代方法是读取最后 X 个字节,将它们转换为字符串并将其拆分为行。如果您有 UTF8 文件,这并不像听起来那么容易,因为块可能会丢失第一个字符的第一个字节。 This question asks how to do this 和 UTF8 留作 reader.
的练习
要检索 IEnumerable<T>
中的最后 N 项,您可以使用 .NET Core 引入的 TakeLast 方法:
var lastLines = File.ReadLines("SomeFile.log").TakeLast(2);
还有SkipLast,所以如果你想要倒数第二行,你可以使用:
var secondLast = File.ReadLines("SomeFile.log").TakeLast(2).SkipLast(1);
尽管只有 1 行,TakeLast(2).FirstOrDefault()
就足够了。
对于 .NET Framework,您可以使用类似 this answer's code or this one 的方法来迭代并保留最后 N 行:
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
if (source == null) { throw new ArgumentNullException("source"); }
Queue<T> lastElements = new Queue<T>();
foreach (T element in source)
{
lastElements.Enqueue(element);
if (lastElements.Count > count)
{
lastElements.Dequeue();
}
}
return lastElements;
}
此代码需要稍作改动才能成为 SkipLast()
,返回出列的项目而不是丢弃它们:
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
if (source == null) { throw new ArgumentNullException("source"); }
Queue<T> lastElements = new Queue<T>();
foreach (T element in source)
{
lastElements.Enqueue(element);
if (lastElements.Count > count)
{
var head=lastElements.Dequeue();
yield return head;
}
}
}
我有一个日志文件,我正试图从中获取一些信息。我需要的信息在最后一行之前的那一行,最后一行 is/could 是空白的。所以如果最后一行是空白,它实际上是最后一行之前的一行或最后一行之前的两行。
我知道如何使用以下方法到达文件的最后一行:
var lastLine = File.ReadLines("SomeFile.log").Last();
我还可以使用 Linq 使用 .skipWhile() 或 .skip(1) 来跳过行,但不会向后移动。
我不确定如何到达我需要的线路。这是日志文件最后几行的示例(最后一行为空白):
2021/05/02 23:47:57:008989 send_status_message(2) Info: "Stream status heartbeat sent: [SY 1.3.2 ]"
2021/05/02 23:47:57:225172 send_status_message(2) Info: "Received heartbeat response: [S ]"
2021/05/03 00:00:00:045055 set_log_dir(2) Info: "Changing log directory to /abc/def/logs/2021-05-03."
<blank-line>
我正在尝试获取该行的时间戳(即 2021/05/02 23:47:57:225172)。
像这样的东西可能适合你
var lines = System.IO.File.ReadLines(@"SomeFile.log");
var secondLastIdx = lines.Count() - 2;
var secondlast = lines.Skip(secondLastIdx ).First();
您可能需要使用更好的方法来计算 secondLastIdx
使用 C#8 的 range operator
如果你的数组已经在内存中并且可以使用 C# 8,你可以这样做:
var Lines = File.ReadAllLines("SomeFile.log");
var SecondToLast = Lines[^2];
没有 C#8。
或者,如 Tim 所述,您可以在索引器上进行算术运算:
var Lines = File.ReadAllLines("SomeFile.log");
var SecondToLast = Lines[Lines.Length - 2];
基于评论的编辑。 从您的评论来看,您似乎不太确定会得到多少空行。如果是这种情况,您最好使用更通用的方法,例如:
static string FirstNotEmpty(string[] Lines, bool BottomUp = false)
{
if (BottomUp)
{
for (int i = Lines.Length - 1; i >= 0; i--)
{
var CurrentLine = Lines[i];
if (!string.IsNullOrWhiteSpace(CurrentLine))
return CurrentLine;
}
}
else
{
for (int i = 0; i <= Lines.Length-1; i++)
{
var CurrentLine = Lines[i];
if (!string.IsNullOrWhiteSpace(CurrentLine))
return CurrentLine;
}
}
return null; //Or something else.
}
在你的情况下,你会这样称呼它:
var FirstNotEmptyLine = FirstNotEmpty(Lines, BottomUp: true);
您也可以先发制人地从数组中删除空行:
var WithoutEmptyLines = Lines.Where(x => !string.IsNullOrWhiteSpace(x));
然后“安全地”获取最后一行。
也许你可以使用这个扩展方法:
public static class EnumerableExtensions
{
public static T GetLastItem<T>(this IEnumerable<T> seq, int countFromEnd)
{
if(seq is IList<T> list) return list[^countFromEnd];
using var enumerator = seq.Reverse().GetEnumerator();
while(enumerator.MoveNext())
{
if(--countFromEnd == 0) return enumerator.Current;
}
throw new ArgumentOutOfRangeException();
}
}
用法:
var secondLastLine = File.ReadLines("SomeFile.log").GetLastItem(2);
如果你不使用C#8,那么你不能使用Ranges,将return list[^countFromEnd]
替换为return list[list.Count - countFromEnd]
。
File.ReadLines("SomeFile.log").Last();
将遍历所有行并保留最后一行。这对于大文件来说可能很昂贵。至少它不会将它们全部保留在内存中。
更快的替代方法是读取最后 X 个字节,将它们转换为字符串并将其拆分为行。如果您有 UTF8 文件,这并不像听起来那么容易,因为块可能会丢失第一个字符的第一个字节。 This question asks how to do this 和 UTF8 留作 reader.
的练习要检索 IEnumerable<T>
中的最后 N 项,您可以使用 .NET Core 引入的 TakeLast 方法:
var lastLines = File.ReadLines("SomeFile.log").TakeLast(2);
还有SkipLast,所以如果你想要倒数第二行,你可以使用:
var secondLast = File.ReadLines("SomeFile.log").TakeLast(2).SkipLast(1);
尽管只有 1 行,TakeLast(2).FirstOrDefault()
就足够了。
对于 .NET Framework,您可以使用类似 this answer's code or this one 的方法来迭代并保留最后 N 行:
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
if (source == null) { throw new ArgumentNullException("source"); }
Queue<T> lastElements = new Queue<T>();
foreach (T element in source)
{
lastElements.Enqueue(element);
if (lastElements.Count > count)
{
lastElements.Dequeue();
}
}
return lastElements;
}
此代码需要稍作改动才能成为 SkipLast()
,返回出列的项目而不是丢弃它们:
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
if (source == null) { throw new ArgumentNullException("source"); }
Queue<T> lastElements = new Queue<T>();
foreach (T element in source)
{
lastElements.Enqueue(element);
if (lastElements.Count > count)
{
var head=lastElements.Dequeue();
yield return head;
}
}
}