C#文本文件读取ObjectDisposedException:对象在被释放后被使用
我想读取文本文件的最后一行。我正在使用此处建议的解决方案: 使用该库时,我得到一个错误,表示流已被释放。但是我很困惑,因为我在每一帧中声明流C#文本文件读取ObjectDisposedException:对象在被释放后被使用,c#,file,stream,C#,File,Stream,我想读取文本文件的最后一行。我正在使用此处建议的解决方案: 使用该库时,我得到一个错误,表示流已被释放。但是我很困惑,因为我在每一帧中声明流 FileStream fileStream = new FileStream("C:\\Users\\LukasRoper\\Desktop\\Test.log", FileMode.Open, FileAccess.Read, FileShare.ReadWrite); ReverseLineReader reverseLineReader = n
FileStream fileStream = new FileStream("C:\\Users\\LukasRoper\\Desktop\\Test.log", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
ReverseLineReader reverseLineReader = new ReverseLineReader(() => fileStream, Encoding.UTF8);
List<string> stringParts = new List<string>();
do
{
IEnumerable<string> line = reverseLineReader.Take(1);
string data = line.First();
stringParts = data.Split(',').ToList();
} while (stringParts.Count != 9);
FileStream FileStream=newfilestream(“C:\\Users\\lukastroper\\Desktop\\Test.log”,FileMode.Open,FileAccess.Read,FileShare.ReadWrite);
ReverseLineReader ReverseLineReader=新的ReverseLineReader(()=>fileStream,Encoding.UTF8);
List stringParts=新列表();
做
{
IEnumerable行=反向线读取(1);
字符串数据=line.First();
stringParts=data.Split(',').ToList();
}而(stringParts.Count!=9);
我应该解释一下,我试图从另一个程序同时写入的文件中读取数据,我不能将该程序修改为其第三方软件。有人能解释一下为什么我的文件流被丢弃了吗
反向文件读取器位于此处:
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace MiscUtil.IO
{
/// <summary>
/// Takes an encoding (defaulting to UTF-8) and a function which produces a seekable stream
/// (or a filename for convenience) and yields lines from the end of the stream backwards.
/// Only single byte encodings, and UTF-8 and Unicode, are supported. The stream
/// returned by the function must be seekable.
/// </summary>
public sealed class ReverseLineReader : IEnumerable<string>
{
/// <summary>
/// Buffer size to use by default. Classes with internal access can specify
/// a different buffer size - this is useful for testing.
/// </summary>
private const int DefaultBufferSize = 4096;
/// <summary>
/// Means of creating a Stream to read from.
/// </summary>
private readonly Func<Stream> streamSource;
/// <summary>
/// Encoding to use when converting bytes to text
/// </summary>
private readonly Encoding encoding;
/// <summary>
/// Size of buffer (in bytes) to read each time we read from the
/// stream. This must be at least as big as the maximum number of
/// bytes for a single character.
/// </summary>
private readonly int bufferSize;
/// <summary>
/// Function which, when given a position within a file and a byte, states whether
/// or not the byte represents the start of a character.
/// </summary>
private Func<long,byte,bool> characterStartDetector;
/// <summary>
/// Creates a LineReader from a stream source. The delegate is only
/// called when the enumerator is fetched. UTF-8 is used to decode
/// the stream into text.
/// </summary>
/// <param name="streamSource">Data source</param>
public ReverseLineReader(Func<Stream> streamSource)
: this(streamSource, Encoding.UTF8)
{
}
/// <summary>
/// Creates a LineReader from a filename. The file is only opened
/// (or even checked for existence) when the enumerator is fetched.
/// UTF8 is used to decode the file into text.
/// </summary>
/// <param name="filename">File to read from</param>
public ReverseLineReader(string filename)
: this(filename, Encoding.UTF8)
{
}
/// <summary>
/// Creates a LineReader from a filename. The file is only opened
/// (or even checked for existence) when the enumerator is fetched.
/// </summary>
/// <param name="filename">File to read from</param>
/// <param name="encoding">Encoding to use to decode the file into text</param>
public ReverseLineReader(string filename, Encoding encoding)
: this(() => File.OpenRead(filename), encoding)
{
}
/// <summary>
/// Creates a LineReader from a stream source. The delegate is only
/// called when the enumerator is fetched.
/// </summary>
/// <param name="streamSource">Data source</param>
/// <param name="encoding">Encoding to use to decode the stream into text</param>
public ReverseLineReader(Func<Stream> streamSource, Encoding encoding)
: this(streamSource, encoding, DefaultBufferSize)
{
}
internal ReverseLineReader(Func<Stream> streamSource, Encoding encoding, int bufferSize)
{
this.streamSource = streamSource;
this.encoding = encoding;
this.bufferSize = bufferSize;
if (encoding.IsSingleByte)
{
// For a single byte encoding, every byte is the start (and end) of a character
characterStartDetector = (pos, data) => true;
}
else if (encoding is UnicodeEncoding)
{
// For UTF-16, even-numbered positions are the start of a character
characterStartDetector = (pos, data) => (pos & 1) == 0;
}
else if (encoding is UTF8Encoding)
{
// For UTF-8, bytes with the top bit clear or the second bit set are the start of a character
// See http://www.cl.cam.ac.uk/~mgk25/unicode.html
characterStartDetector = (pos, data) => (data & 0x80) == 0 || (data & 0x40) != 0;
}
else
{
throw new ArgumentException("Only single byte, UTF-8 and Unicode encodings are permitted");
}
}
/// <summary>
/// Returns the enumerator reading strings backwards. If this method discovers that
/// the returned stream is either unreadable or unseekable, a NotSupportedException is thrown.
/// </summary>
public IEnumerator<string> GetEnumerator()
{
Stream stream = streamSource();
if (!stream.CanSeek)
{
stream.Dispose();
throw new NotSupportedException("Unable to seek within stream");
}
if (!stream.CanRead)
{
stream.Dispose();
throw new NotSupportedException("Unable to read within stream");
}
return GetEnumeratorImpl(stream);
}
private IEnumerator<string> GetEnumeratorImpl(Stream stream)
{
try
{
long position = stream.Length;
if (encoding is UnicodeEncoding && (position & 1) != 0)
{
throw new InvalidDataException("UTF-16 encoding provided, but stream has odd length.");
}
// Allow up to two bytes for data from the start of the previous
// read which didn't quite make it as full characters
byte[] buffer = new byte[bufferSize + 2];
char[] charBuffer = new char[encoding.GetMaxCharCount(buffer.Length)];
int leftOverData = 0;
String previousEnd = null;
// TextReader doesn't return an empty string if there's line break at the end
// of the data. Therefore we don't return an empty string if it's our *first*
// return.
bool firstYield = true;
// A line-feed at the start of the previous buffer means we need to swallow
// the carriage-return at the end of this buffer - hence this needs declaring
// way up here!
bool swallowCarriageReturn = false;
while (position > 0)
{
int bytesToRead = Math.Min(position > int.MaxValue ? bufferSize : (int)position, bufferSize);
position -= bytesToRead;
stream.Position = position;
StreamUtil.ReadExactly(stream, buffer, bytesToRead);
// If we haven't read a full buffer, but we had bytes left
// over from before, copy them to the end of the buffer
if (leftOverData > 0 && bytesToRead != bufferSize)
{
// Buffer.BlockCopy doesn't document its behaviour with respect
// to overlapping data: we *might* just have read 7 bytes instead of
// 8, and have two bytes to copy...
Array.Copy(buffer, bufferSize, buffer, bytesToRead, leftOverData);
}
// We've now *effectively* read this much data.
bytesToRead += leftOverData;
int firstCharPosition = 0;
while (!characterStartDetector(position + firstCharPosition, buffer[firstCharPosition]))
{
firstCharPosition++;
// Bad UTF-8 sequences could trigger this. For UTF-8 we should always
// see a valid character start in every 3 bytes, and if this is the start of the file
// so we've done a short read, we should have the character start
// somewhere in the usable buffer.
if (firstCharPosition == 3 || firstCharPosition == bytesToRead)
{
throw new InvalidDataException("Invalid UTF-8 data");
}
}
leftOverData = firstCharPosition;
int charsRead = encoding.GetChars(buffer, firstCharPosition, bytesToRead - firstCharPosition, charBuffer, 0);
int endExclusive = charsRead;
for (int i = charsRead - 1; i >= 0; i--)
{
char lookingAt = charBuffer[i];
if (swallowCarriageReturn)
{
swallowCarriageReturn = false;
if (lookingAt == '\r')
{
endExclusive--;
continue;
}
}
// Anything non-line-breaking, just keep looking backwards
if (lookingAt != '\n' && lookingAt != '\r')
{
continue;
}
// End of CRLF? Swallow the preceding CR
if (lookingAt == '\n')
{
swallowCarriageReturn = true;
}
int start = i + 1;
string bufferContents = new string(charBuffer, start, endExclusive - start);
endExclusive = i;
string stringToYield = previousEnd == null ? bufferContents : bufferContents + previousEnd;
if (!firstYield || stringToYield.Length != 0)
{
yield return stringToYield;
}
firstYield = false;
previousEnd = null;
}
previousEnd = endExclusive == 0 ? null : (new string(charBuffer, 0, endExclusive) + previousEnd);
// If we didn't decode the start of the array, put it at the end for next time
if (leftOverData != 0)
{
Buffer.BlockCopy(buffer, 0, buffer, bufferSize, leftOverData);
}
}
if (leftOverData != 0)
{
// At the start of the final buffer, we had the end of another character.
throw new InvalidDataException("Invalid UTF-8 data at start of stream");
}
if (firstYield && string.IsNullOrEmpty(previousEnd))
{
yield break;
}
yield return previousEnd ?? "";
}
finally
{
stream.Dispose();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
使用系统;
使用系统集合;
使用System.Collections.Generic;
使用System.IO;
使用系统文本;
命名空间MiscUtil.IO
{
///
///采用编码(默认为UTF-8)和生成可查找流的函数
///(或为方便起见使用文件名)并从流的末尾向后生成行。
///仅支持单字节编码、UTF-8和Unicode
///函数返回的值必须是可查找的。
///
公共密封类反向读取器:IEnumerable
{
///
///默认情况下使用的缓冲区大小。具有内部访问权限的类可以指定
///不同的缓冲区大小-这对于测试很有用。
///
private const int DefaultBufferSize=4096;
///
///创建要从中读取的流的方法。
///
私有只读Func streamSource;
///
///将字节转换为文本时要使用的编码
///
私有只读编码;
///
///每次从中读取时要读取的缓冲区大小(以字节为单位)
///流。这必须至少与流的最大数量相同
///单个字符的字节数。
///
私有只读int-bufferSize;
///
///函数,当给定文件中的一个位置和一个字节时,该函数说明
///字节是否表示字符的开头。
///
私有函数characterStartDetector;
///
///从流源创建LineReader。委托仅为
///获取枚举数时调用。UTF-8用于解码
///将流转换为文本。
///
///数据源
公共反向读卡器(Func streamSource)
:此(streamSource,Encoding.UTF8)
{
}
///
///从文件名创建行读取器。仅打开该文件
///获取枚举数时(甚至检查是否存在)。
///UTF8用于将文件解码为文本。
///
///要从中读取的文件
公共ReverseLineReader(字符串文件名)
:此(文件名,Encoding.UTF8)
{
}
///
///从文件名创建行读取器。仅打开该文件
///获取枚举数时(甚至检查是否存在)。
///
///要从中读取的文件
///用于将文件解码为文本的编码
公共ReverseLineReader(字符串文件名,编码)
:此(()=>File.OpenRead(文件名),编码)
{
}
///
///从流源创建LineReader。委托仅为
///获取枚举数时调用。
///
///数据源
///用于将流解码为文本的编码
公共ReverseLineReader(Func streamSource,编码)
:此(streamSource、编码、DefaultBufferSize)
{
}
内部ReverseLineReader(Func streamSource、编码、int bufferSize)
{
this.streamSource=streamSource;
this.encoding=编码;
this.bufferSize=bufferSize;
if(编码.IsSingleByte)
{
//对于单字节编码,每个字节都是字符的开始(和结束)
characterStartDetector=(位置、数据)=>true;
}
else if(编码为Unicode编码)
{
//对于UTF-16,偶数编号的位置是字符的开头
characterStartDetector=(位置,数据)=>(位置&1)==0;
}
else if(编码为UTF8Encoding)
{
//对于UTF-8,具有顶部位清除或第二位设置的字节是字符的开头
//看http://www.cl.cam.ac.uk/~mgk25/unicode.html
characterStartDetector=(位置,数据)=>(数据&0x80)==0 | |(数据&0x40)!=0;
}
其他的
{
抛出新的ArgumentException(“只允许使用单字节、UTF-8和Unicode编码”);
}
}
///
///返回向后读取字符串的枚举数。如果此方法发现
///返回的流不可读或不可查找,将引发NotSupportedException。
///
公共IEnumerator GetEnumerator()
{
Stream=streamSource();
if(!stream.CanSeek)
{
stream.Dispose();
抛出新的NotSupportedException(“无法在流中查找”);
}
如果(!stream.CanRead)
{
stream.Dispose();
抛出新的NotSupportedException(“无法在流中读取”);
}
返回GetEnumeratorImpl(流);
}
私有IEnumerator GetEnumeratorImpl(流)
{
尝试
{
长位置=流长度;
如果(编码为Unicode编码&(位置&1)!=0)
{
抛出新的InvalidDataException(“提供了UTF-16编码,但流具有奇数长度”);
}
//最多允许两个字节用于上一个操作开始时的数据
//读一读并不是很完整的c
using (FileStream fileStream = new FileStream(...))
{
...
do
{
...
} while(...);
}