diff --git a/Corlib.Extensions/System/IO/FileInfo.cs b/Corlib.Extensions/System/IO/FileInfo.cs
index b21ad297..e0a0343c 100644
--- a/Corlib.Extensions/System/IO/FileInfo.cs
+++ b/Corlib.Extensions/System/IO/FileInfo.cs
@@ -1704,7 +1704,23 @@ private static void _KeepFirstLines(FileInfo @this, int count, Encoding encoding
///
/// This example keeps the last 5 lines of "example.txt" and removes all other preceding lines.
///
- public static void KeepLastLines(this FileInfo @this, int count) => _KeepLastLines(@this, count, null, LineBreakMode.AutoDetect);
+ public static void KeepLastLines(this FileInfo @this, int count) => _KeepLastLines(@this, count, null, LineBreakMode.AutoDetect, 0);
+
+ ///
+ /// Keeps only the specified number of last lines in the file, discarding the rest.
+ ///
+ /// The object representing the file.
+ /// The number of lines from the end of the file to keep.
+ /// The number of lines to keep at the start of the file.
+ ///
+ ///
+ /// FileInfo fileInfo = new FileInfo("example.txt");
+ /// fileInfo.KeepLastLines(5, 1);
+ /// Console.WriteLine("Last 5 lines and the first kept, others removed.");
+ ///
+ /// This example keeps the last 5 lines and the first of "example.txt" and removes all other preceding lines.
+ ///
+ public static void KeepLastLines(this FileInfo @this, int count, int offsetInLines) => _KeepLastLines(@this, count, null, LineBreakMode.AutoDetect, offsetInLines);
///
/// Keeps only the specified number of last lines in the file, discarding the rest, using the provided encoding.
@@ -1723,7 +1739,28 @@ private static void _KeepFirstLines(FileInfo @this, int count, Encoding encoding
public static void KeepLastLines(this FileInfo @this, int count, Encoding encoding) {
Against.ArgumentIsNull(encoding);
- _KeepLastLines(@this, count, encoding, LineBreakMode.AutoDetect);
+ _KeepLastLines(@this, count, encoding, LineBreakMode.AutoDetect, 0);
+ }
+
+ ///
+ /// Keeps only the specified number of last lines in the file, discarding the rest, using the provided encoding.
+ ///
+ /// The object representing the file.
+ /// The number of lines from the end of the file to keep.
+ /// The number of lines to keep at the start of the file.
+ /// The encoding to use for interpreting the file's content.
+ ///
+ ///
+ /// FileInfo fileInfo = new FileInfo("example.txt");
+ /// fileInfo.KeepLastLines(5, 1, Encoding.UTF8);
+ /// Console.WriteLine("Last 5 lines and the first kept using UTF-8 encoding, others removed.");
+ ///
+ /// This example keeps the last 5 lines and the first of "example.txt" using UTF-8 encoding and removes all other preceding lines.
+ ///
+ public static void KeepLastLines(this FileInfo @this, int count, int offsetInLines, Encoding encoding) {
+ Against.ArgumentIsNull(encoding);
+
+ _KeepLastLines(@this, count, encoding, LineBreakMode.AutoDetect, offsetInLines);
}
///
@@ -1740,7 +1777,24 @@ public static void KeepLastLines(this FileInfo @this, int count, Encoding encodi
///
/// This example keeps the last 5 lines of "example.txt" based on CrLf line breaks and removes all other preceding lines.
///
- public static void KeepLastLines(this FileInfo @this, int count, LineBreakMode newLine) => _KeepLastLines(@this, count, null, newLine);
+ public static void KeepLastLines(this FileInfo @this, int count, LineBreakMode newLine) => _KeepLastLines(@this, count, null, newLine, 0);
+
+ ///
+ /// Keeps only the specified number of last lines in the file, discarding the rest, based on the specified line break mode.
+ ///
+ /// The object representing the file.
+ /// The number of lines from the end of the file to keep.
+ /// The number of lines to keep at the start of the file.
+ /// The line break mode to determine the line endings in the file.
+ ///
+ ///
+ /// FileInfo fileInfo = new FileInfo("example.txt");
+ /// fileInfo.KeepLastLines(5, 1, LineBreakMode.CrLf);
+ /// Console.WriteLine("Last 5 lines and the first kept using CrLf line breaks, others removed.");
+ ///
+ /// This example keeps the last 5 lines and the first of "example.txt" based on CrLf line breaks and removes all other preceding lines.
+ ///
+ public static void KeepLastLines(this FileInfo @this, int count, int offsetInLines, LineBreakMode newLine) => _KeepLastLines(@this, count, null, newLine, offsetInLines);
///
/// Keeps only the specified number of last lines in the file, discarding the rest, using the provided encoding and line break mode.
@@ -1760,13 +1814,36 @@ public static void KeepLastLines(this FileInfo @this, int count, Encoding encodi
public static void KeepLastLines(this FileInfo @this, int count, Encoding encoding, LineBreakMode newLine) {
Against.ArgumentIsNull(encoding);
- _KeepLastLines(@this, count, encoding, newLine);
+ _KeepLastLines(@this, count, encoding, newLine, 0);
+ }
+
+ ///
+ /// Keeps only the specified number of last lines in the file, discarding the rest, using the provided encoding and line break mode.
+ ///
+ /// The object representing the file.
+ /// The number of lines from the end of the file to keep.
+ /// The number of lines to keep at the start of the file.
+ /// The encoding to use for interpreting the file's content.
+ /// The line break mode to determine the line endings in the file.
+ ///
+ ///
+ /// FileInfo fileInfo = new FileInfo("example.txt");
+ /// fileInfo.KeepLastLines(5, 1, Encoding.UTF8, LineBreakMode.CrLf);
+ /// Console.WriteLine("Last 5 lines and the first kept using UTF-8 encoding and CrLf line breaks, others removed.");
+ ///
+ /// This example keeps the last 5 lines and the first of "example.txt" using UTF-8 encoding and CrLf line breaks, removing all other preceding lines.
+ ///
+ public static void KeepLastLines(this FileInfo @this, int count, int offsetInLines, Encoding encoding, LineBreakMode newLine) {
+ Against.ArgumentIsNull(encoding);
+
+ _KeepLastLines(@this, count, encoding, newLine, offsetInLines);
}
- private static void _KeepLastLines(FileInfo @this, int count, Encoding encoding, LineBreakMode newLine) {
+ private static void _KeepLastLines(FileInfo @this, int count, Encoding encoding, LineBreakMode newLine, int offsetInLines) {
Against.ThisIsNull(@this);
Against.CountBelowOrEqualZero(count);
Against.UnknownEnumValues(newLine);
+ Against.NegativeValues(offsetInLines);
using var stream = @this.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
var linePositions = new long[count];
@@ -1778,6 +1855,10 @@ private static void _KeepLastLines(FileInfo @this, int count, Encoding encoding,
: new(stream, encoding, newLine)
;
+ var writePosition = reader.PreambleSize;
+ while (offsetInLines-- > 0 && reader.ReadLine()!=null)
+ writePosition = stream.Position;
+
for (;;) {
var startOfLine = stream.Position;
if (reader.ReadLine() == null)
@@ -1792,7 +1873,6 @@ private static void _KeepLastLines(FileInfo @this, int count, Encoding encoding,
return;
--readPosition;
- var writePosition = reader.PreambleSize;
const int bufferSize = 64 * 1024;
var buffer = new byte[bufferSize];
diff --git a/Corlib.Extensions/System/IO/FileInfoExtensions.CustomTextReader.cs b/Corlib.Extensions/System/IO/FileInfoExtensions.CustomTextReader.cs
index 278bafe6..ddc1cc7e 100644
--- a/Corlib.Extensions/System/IO/FileInfoExtensions.CustomTextReader.cs
+++ b/Corlib.Extensions/System/IO/FileInfoExtensions.CustomTextReader.cs
@@ -323,9 +323,9 @@ public Initialized(Stream stream, bool detectEncodingFromByteOrderMark, StringEx
stream, detectEncodingFromByteOrderMark, null, lineBreakMode) { }
public Initialized(Stream stream, Encoding encoding, StringExtensions.LineBreakMode lineBreakMode = StringExtensions.LineBreakMode.AutoDetect)
- : this(stream, false, encoding, lineBreakMode) {
- Against.ArgumentIsNull(encoding);
- }
+ : this(stream, false, encoding, lineBreakMode)
+ => Against.ArgumentIsNull(encoding)
+ ;
public long PreambleSize { get; }
diff --git a/Tests/Corlib.Tests/System/IO/FileInfoTest.cs b/Tests/Corlib.Tests/System/IO/FileInfoTest.cs
index ab09e30a..e0f5e858 100644
--- a/Tests/Corlib.Tests/System/IO/FileInfoTest.cs
+++ b/Tests/Corlib.Tests/System/IO/FileInfoTest.cs
@@ -335,43 +335,54 @@ public enum TestEncoding {
[TestCase("ab\nc\x0076def", 1, TestEncoding.Utf8, LineBreakMode.Zx, "ab\nc\x76")]
[TestCase("ab\nc\0def", 1, TestEncoding.Utf8, LineBreakMode.Null, "ab\nc\0")]
public void KeepFirstLines(string? input, int count, TestEncoding testEncoding, LineBreakMode newLine, string expected, Type? exception = null)
- => this._ExecuteTest((f, c, e, l, a) => {
+ => this._ExecuteTest((f, c, e, l, o,a) => {
if (a)
f.KeepFirstLines(c, l);
else
f.KeepFirstLines(c, e, l);
- }, input, count, testEncoding, newLine, expected, exception)
+ }, input, count, testEncoding, newLine, 0,expected, exception)
;
[Test]
- [TestCase(null, 1, TestEncoding.Utf8, LineBreakMode.LineFeed, null, typeof(NullReferenceException))]
- [TestCase("", 0, TestEncoding.Utf8, LineBreakMode.LineFeed, null, typeof(ArgumentOutOfRangeException))]
- [TestCase("", 1, TestEncoding.Null, LineBreakMode.LineFeed, null, typeof(ArgumentNullException))]
- [TestCase("abc", 1, TestEncoding.ASCII, (LineBreakMode)short.MinValue, "", typeof(ArgumentException))]
- [TestCase("abc", 1, TestEncoding.ASCII, LineBreakMode.None, "abc")]
- [TestCase("abc\n", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, "abc\n")]
- [TestCase("abc\ndef", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, "def")]
- [TestCase("abc\ndef\n", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, "def\n")]
- [TestCase("abc\r\ndef", 1, TestEncoding.ASCII, LineBreakMode.CrLf, "def")]
- [TestCase("abc\r\ndef\r\n", 1, TestEncoding.ASCII, LineBreakMode.CrLf, "def\r\n")]
- [TestCase("abc\r\ndef", 1, TestEncoding.ASCII, LineBreakMode.All, "def")]
- [TestCase("abc\r\ndef", 1, TestEncoding.ASCII, LineBreakMode.AutoDetect, "def")]
- [TestCase("abc\rdef\r", 1, TestEncoding.AutoDetectFromBom, LineBreakMode.CarriageReturn, "def\r")]
- [TestCase("abc\x000cdef", 1, TestEncoding.UnicodeBigEndian, LineBreakMode.FormFeed, "def")]
- [TestCase("abc\x0085de\ff", 1, TestEncoding.UnicodeLittleEndianNoBOM, LineBreakMode.NextLine, "de\ff")]
- [TestCase("abc\x0015de\u0085f", 1, TestEncoding.Utf8, LineBreakMode.NegativeAcknowledge, "de\u0085f")]
- [TestCase("abc\x2028de\rf", 1, TestEncoding.Utf8NoBOM, LineBreakMode.LineSeparator, "de\rf")]
- [TestCase("abc\x2029de\nf", 1, TestEncoding.Utf8, LineBreakMode.ParagraphSeparator, "de\nf")]
- [TestCase("abc\x009Bde\nf", 1, TestEncoding.Utf8, LineBreakMode.EndOfLine, "de\nf")]
- [TestCase("abc\x0076de\nf", 1, TestEncoding.Utf8, LineBreakMode.Zx, "de\nf")]
- [TestCase("abc\0de\nf", 1, TestEncoding.Utf8, LineBreakMode.Null, "de\nf")]
- public void KeepLastLines(string? input, int count, TestEncoding testEncoding, LineBreakMode newLine, string expected, Type? exception = null)
- => this._ExecuteTest((f, c, e, l, a) => {
- if (a)
- f.KeepLastLines(c, l);
- else
- f.KeepLastLines(c, e, l);
- }, input, count, testEncoding, newLine, expected, exception)
+ [TestCase(null, 1, TestEncoding.Utf8, LineBreakMode.LineFeed, 0, null, typeof(NullReferenceException))]
+ [TestCase("", 0, TestEncoding.Utf8, LineBreakMode.LineFeed, 0, null, typeof(ArgumentOutOfRangeException))]
+ [TestCase("", 1, TestEncoding.Null, LineBreakMode.LineFeed, 0, null, typeof(ArgumentNullException))]
+ [TestCase("abc", 1, TestEncoding.ASCII, (LineBreakMode)short.MinValue, 0, "", typeof(ArgumentException))]
+ [TestCase("abc", 1, TestEncoding.ASCII, LineBreakMode.None, 0, "abc")]
+ [TestCase("abc\n", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, 0, "abc\n")]
+ [TestCase("abc\ndef", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, 0, "def")]
+ [TestCase("abc\ndef\n", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, 0, "def\n")]
+ [TestCase("abc\r\ndef", 1, TestEncoding.ASCII, LineBreakMode.CrLf, 0, "def")]
+ [TestCase("abc\r\ndef\r\n", 1, TestEncoding.ASCII, LineBreakMode.CrLf, 0, "def\r\n")]
+ [TestCase("abc\r\ndef", 1, TestEncoding.ASCII, LineBreakMode.All, 0, "def")]
+ [TestCase("abc\r\ndef", 1, TestEncoding.ASCII, LineBreakMode.AutoDetect, 0, "def")]
+ [TestCase("abc\rdef\r", 1, TestEncoding.AutoDetectFromBom, LineBreakMode.CarriageReturn, 0, "def\r")]
+ [TestCase("abc\x000cdef", 1, TestEncoding.UnicodeBigEndian, LineBreakMode.FormFeed, 0, "def")]
+ [TestCase("abc\x0085de\ff", 1, TestEncoding.UnicodeLittleEndianNoBOM, LineBreakMode.NextLine, 0, "de\ff")]
+ [TestCase("abc\x0015de\u0085f", 1, TestEncoding.Utf8, LineBreakMode.NegativeAcknowledge, 0, "de\u0085f")]
+ [TestCase("abc\x2028de\rf", 1, TestEncoding.Utf8NoBOM, LineBreakMode.LineSeparator, 0, "de\rf")]
+ [TestCase("abc\x2029de\nf", 1, TestEncoding.Utf8, LineBreakMode.ParagraphSeparator, 0, "de\nf")]
+ [TestCase("abc\x009Bde\nf", 1, TestEncoding.Utf8, LineBreakMode.EndOfLine, 0, "de\nf")]
+ [TestCase("abc\x0076de\nf", 1, TestEncoding.Utf8, LineBreakMode.Zx, 0, "de\nf")]
+ [TestCase("abc\0de\nf", 1, TestEncoding.Utf8, LineBreakMode.Null, 0, "de\nf")]
+ [TestCase("abc\n", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, -1, "abc\n",typeof(ArgumentOutOfRangeException))]
+ [TestCase("abc\n", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, 1, "abc\n")]
+ [TestCase("abc\ndef\n", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, 1, "abc\ndef\n")]
+ [TestCase("abc\ndef\nghi\n", 1, TestEncoding.ASCII, LineBreakMode.LineFeed, 1, "abc\nghi\n")]
+ public void KeepLastLines(string? input, int count, TestEncoding testEncoding, LineBreakMode newLine, int offset, string expected, Type? exception = null)
+ => this._ExecuteTest((f, c, e, l, o, a) => {
+ if (o != 0) {
+ if (a)
+ f.KeepLastLines(c, o, l);
+ else
+ f.KeepLastLines(c, o, e, l);
+ } else{
+ if (a)
+ f.KeepLastLines(c, l);
+ else
+ f.KeepLastLines(c, e, l);
+ }
+ }, input, count, testEncoding, newLine, offset, expected, exception)
;
[Test]
@@ -397,12 +408,12 @@ public void KeepLastLines(string? input, int count, TestEncoding testEncoding, L
[TestCase("abc\x0076de\nf", 1, TestEncoding.Utf8, LineBreakMode.Zx, "de\nf")]
[TestCase("abc\0de\nf", 1, TestEncoding.Utf8, LineBreakMode.Null, "de\nf")]
public void RemoveFirstLines(string? input, int count, TestEncoding testEncoding, LineBreakMode newLine, string expected, Type? exception = null)
- => this._ExecuteTest((f, c, e, l, a) => {
+ => this._ExecuteTest((f, c, e, l,o, a) => {
if (a)
f.RemoveFirstLines(c, l);
else
f.RemoveFirstLines(c, e, l);
- }, input, count, testEncoding, newLine, expected, exception)
+ }, input, count, testEncoding, newLine,0, expected, exception)
;
[Test]
@@ -428,15 +439,15 @@ public void RemoveFirstLines(string? input, int count, TestEncoding testEncoding
[TestCase("ab\nc\x0076def", 1, TestEncoding.Utf8, LineBreakMode.Zx, "ab\nc\x76")]
[TestCase("ab\nc\0def", 1, TestEncoding.Utf8, LineBreakMode.Null, "ab\nc\0")]
public void RemoveLastLines(string? input,int count, TestEncoding testEncoding, LineBreakMode newLine,string expected, Type? exception=null)
- => this._ExecuteTest((f, c, e, l, a) => {
+ => this._ExecuteTest((f, c, e, l,o, a) => {
if (a)
f.RemoveLastLines(c, l);
else
f.RemoveLastLines(c, e, l);
- }, input,count,testEncoding,newLine,expected,exception)
+ }, input,count,testEncoding,newLine,0,expected,exception)
;
- private void _ExecuteTest(Action runner, string? input, int count, TestEncoding testEncoding, LineBreakMode newLine, string expected, Type? exception = null) {
+ private void _ExecuteTest(Action runner, string? input, int count, TestEncoding testEncoding, LineBreakMode newLine, int offset, string expected, Type? exception = null) {
Encoding writeEncoding;
Encoding? readEncoding;
switch (testEncoding) {
@@ -471,7 +482,7 @@ private void _ExecuteTest(Action run
if (input == null) {
file = null;
ExecuteTest(() => {
- runner(file, count, readEncoding, newLine,false);
+ runner(file, count, readEncoding, newLine, offset, false);
return file.ReadAllText(writeEncoding);
}, expected, exception);
} else {
@@ -479,13 +490,12 @@ private void _ExecuteTest(Action run
file = token.File;
file.WriteAllText(input, writeEncoding);
ExecuteTest(() => {
- runner(file, count, readEncoding, newLine,testEncoding==TestEncoding.AutoDetectFromBom);
+ runner(file, count, readEncoding, newLine, offset, testEncoding == TestEncoding.AutoDetectFromBom);
return file.ReadAllText(writeEncoding);
}, expected, exception);
}
}
-
}
}
\ No newline at end of file