![](/img/trans.png)
[英]Using StreamReader.ReadLine() to read a specific line number w/o reading entire file
[英]Read a fixed number of bytes when reading file using StreamReader
我正在使用Mozilla字符集檢測器的此端口來確定文件的編碼,然后使用該編碼來構造StreamReader。 到現在為止還挺好。
但是,我正在讀取的文件格式是一種奇怪的格式,因此有時需要跳過一些字節。 就是說,以其他方式編碼的文本文件將在其中嵌入一些原始字節。
我想以文本形式讀取流,直到擊中一些指示字節流跟隨的文本為止,然后我想讀取字節流,然后繼續以文本形式讀取。 做到這一點的最佳方法是什么(簡單性和性能之間的平衡)?
我不能依靠針對StreamReader的FileStream進行搜索(然后丟棄后者中的緩沖數據),因為我不知道到目前為止讀取字符時使用了多少字節。 我可能會放棄使用StreamReader並切換到使用字節和字符的並行數組的定制類,使用解碼器從前者填充字符和字符,並在每次字符讀取時通過使用編碼來跟蹤字節數組中的位置,以進行計算用於字符的字節數。 育。
為了進一步說明,文件具有以下格式:
[編碼字符] [嵌入式字節指示器+ len] [len字節] [編碼字符] ...
在一個零或一個或多個嵌入的字節塊為零且嵌入的char塊可以為任意長度的情況下。
因此,例如:
ABC:123:DEF:456:$ 0099 [0x00,0x01,0x02,... x 99] GHI:789:JKL:...
沒有行定界符。 我可能有由某些字符(在這種情況下為冒號)分隔的任意多個字段(ABC,123,...)。 這些字段可能在各種代碼頁中,包括UTF-8(不保證為單字節)。 當我命中$時,我知道接下來的4個字節包含一個長度(稱為n),接下來的n個字節將被原始讀取,而字節n + 1將是另一個文本字段(GHI)。
概念證明。 此類適用於UTF-16字符串數據和每個OP中的':'分隔符。 它期望二進制長度是一個4字節的低端二進制整數。 應該很容易地將其調整為(奇數)文件格式的更具體的細節。 例如,任何Decoder類都應放入ReadString()並“正常工作”。
要使用它,請使用Stream類構造它。 對於每個單獨的數據元素,調用ReportNextData(),它將告訴您下一個什么樣的數據,然后調用適當的Read *()方法。 對於二進制數據,請調用ReadBinaryLength(),然后調用ReadBinaryData()。
注意,ReadBinaryData()遵循流協定; 它不能保證返回所需的字節數,因此您可能需要多次調用它。 但是,如果您請求太多字節,它將拋出EndOfStreamException。
我使用此數據(十六進制格式)對其進行了測試:410042004300240A0000000102030405060708090024050000000504030201580059005A003A310032003300
這是:ABC $ [10] [1234567890] $ [5] [54321] XYZ:123
像這樣掃描數據:
OddFileReader.NextData nextData;
while ((nextData = reader.ReportNextData()) != OddFileReader.NextData.Eof)
{
// Call appropriate Read*() here.
}
public class OddFileReader : IDisposable
{
public enum NextData
{
Unknown,
Eof,
String,
BinaryLength,
BinaryData
}
private Stream source;
private byte[] byteBuffer;
private int bufferOffset;
private int bufferEnd;
private NextData nextData;
private int binaryOffset;
private int binaryEnd;
private char[] characterBuffer;
public OddFileReader(Stream source)
{
this.source = source;
}
public NextData ReportNextData()
{
if (nextData != NextData.Unknown)
{
return nextData;
}
if (!PopulateBufferIfNeeded(1))
{
return (nextData = NextData.Eof);
}
if (byteBuffer[bufferOffset] == '$')
{
return (nextData = NextData.BinaryLength);
}
else
{
return (nextData = NextData.String);
}
}
public string ReadString()
{
ReportNextData();
if (nextData == NextData.Eof)
{
throw new EndOfStreamException();
}
else if (nextData != NextData.String)
{
throw new InvalidOperationException("Attempt to read non-string data as string");
}
if (characterBuffer == null)
{
characterBuffer = new char[1];
}
StringBuilder stringBuilder = new StringBuilder();
Decoder decoder = Encoding.Unicode.GetDecoder();
while (nextData == NextData.String)
{
byte b = byteBuffer[bufferOffset];
if (b == '$')
{
nextData = NextData.BinaryLength;
break;
}
else if (b == ':')
{
nextData = NextData.Unknown;
bufferOffset++;
break;
}
else
{
if (decoder.GetChars(byteBuffer, bufferOffset++, 1, characterBuffer, 0) == 1)
{
stringBuilder.Append(characterBuffer[0]);
}
if (bufferOffset == bufferEnd && !PopulateBufferIfNeeded(1))
{
nextData = NextData.Eof;
break;
}
}
}
return stringBuilder.ToString();
}
public int ReadBinaryLength()
{
ReportNextData();
if (nextData == NextData.Eof)
{
throw new EndOfStreamException();
}
else if (nextData != NextData.BinaryLength)
{
throw new InvalidOperationException("Attempt to read non-binary-length data as binary length");
}
bufferOffset++;
if (!PopulateBufferIfNeeded(sizeof(Int32)))
{
nextData = NextData.Eof;
throw new EndOfStreamException();
}
binaryEnd = BitConverter.ToInt32(byteBuffer, bufferOffset);
binaryOffset = 0;
bufferOffset += sizeof(Int32);
nextData = NextData.BinaryData;
return binaryEnd;
}
public int ReadBinaryData(byte[] buffer, int offset, int count)
{
ReportNextData();
if (nextData == NextData.Eof)
{
throw new EndOfStreamException();
}
else if (nextData != NextData.BinaryData)
{
throw new InvalidOperationException("Attempt to read non-binary data as binary data");
}
if (count > binaryEnd - binaryOffset)
{
throw new EndOfStreamException();
}
int bytesRead;
if (bufferOffset < bufferEnd)
{
bytesRead = Math.Min(count, bufferEnd - bufferOffset);
Array.Copy(byteBuffer, bufferOffset, buffer, offset, bytesRead);
bufferOffset += bytesRead;
}
else if (count < byteBuffer.Length)
{
if (!PopulateBufferIfNeeded(1))
{
throw new EndOfStreamException();
}
bytesRead = Math.Min(count, bufferEnd - bufferOffset);
Array.Copy(byteBuffer, bufferOffset, buffer, offset, bytesRead);
bufferOffset += bytesRead;
}
else
{
bytesRead = source.Read(buffer, offset, count);
}
binaryOffset += bytesRead;
if (binaryOffset == binaryEnd)
{
nextData = NextData.Unknown;
}
return bytesRead;
}
private bool PopulateBufferIfNeeded(int minimumBytes)
{
if (byteBuffer == null)
{
byteBuffer = new byte[8192];
}
if (bufferEnd - bufferOffset < minimumBytes)
{
int shiftCount = bufferEnd - bufferOffset;
if (shiftCount > 0)
{
Array.Copy(byteBuffer, bufferOffset, byteBuffer, 0, shiftCount);
}
bufferOffset = 0;
bufferEnd = shiftCount;
while (bufferEnd - bufferOffset < minimumBytes)
{
int bytesRead = source.Read(byteBuffer, bufferEnd, byteBuffer.Length - bufferEnd);
if (bytesRead == 0)
{
return false;
}
bufferEnd += bytesRead;
}
}
return true;
}
public void Dispose()
{
Stream source = this.source;
this.source = null;
if (source != null)
{
source.Dispose();
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.