C# - 从特定字符串的文件中读取字节
C# - Read bytes from file from a specific string
我正在尝试用 C# 解析 crg-file。该文件混合了纯文本和二进制数据。文件的第一部分包含纯文本,而文件的其余部分是二进制的(很多浮点数),这是一个示例:
$
$ROAD_CRG
reference_line_start_u = 100
reference_line_end_u = 120
$
$KD_DEFINITION
#:KRBI
U:reference line u,m,730.000,0.010
D:reference line phi,rad
D:long section 1,m
D:long section 2,m
D:long section 3,m
...
$
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
�@z����RA����\�l
...
我知道我可以读取从特定偏移量开始的字节,但我如何找出从哪个字节开始?二进制部分之前的最后一行将始终包含至少四个美元符号“$$$$”。到目前为止,这是我得到的:
using var fs = new FileStream(@"crg_sample.crg", FileMode.Open, FileAccess.Read);
var startByte = ??; // How to find out where to start?
using (BinaryReader reader = new BinaryReader(fs))
{
reader.BaseStream.Seek(startByte, SeekOrigin.Begin);
var f = reader.ReadSingle();
Debug.WriteLine(f);
}
更新:此代码可能无法按预期工作。请在评论中查看有价值的信息。
using (var fs = new FileStream(@"crg_sample.crg", FileMode.Open, FileAccess.Read))
{
using (StreamReader sr = new StreamReader(fs, Encoding.ASCII, true, 1, true))
{
var line = sr.ReadLine();
while (!string.IsNullOrWhiteSpace(line) && !line.Contains("$$$$"))
{
line = sr.ReadLine();
}
}
using (BinaryReader reader = new BinaryReader(fs))
{
// TODO: Start reading the binary data
}
}
当您混合使用文本数据和二进制数据时,您需要将所有内容都视为二进制数据。这意味着您应该使用原始 Stream
访问权限或类似的东西,并使用二进制 APIs 来查看文本数据(通常在 字节处寻找 cr/lf/crlf 作为哨兵,虽然在你的情况下听起来你可以只使用二进制 API 寻找 $$$$
,然后解码整个块,然后向前扫描)。当你认为你有一整行时,然后你可以使用 Encoding
来解析每一行 - 最方便的 API 是 encoding.GetString().
当你完成查看文本数据时 as binary, then 您可以继续解析 binary 数据,再次使用二进制 API。我也会 通常 在这里也反对 BinaryReader
,因为坦率地说,它并没有比更直接的 API 给你带来多少好处。您可能想考虑的 other 问题是 CPU 字节顺序,但假设这不是问题:BitConverter.ToSingle()
可能是您的朋友。
如果数据量不大,您可能会发现使用 byte[]
作为数据最简单;通过 File.ReadAllBytes
,或者从数组池中租用一个超大的 byte[]
,然后从 FileStream
加载它。 Stream
API 对于这种情况很尴尬,因为一旦你查看了数据:它就消失了——所以你需要维护自己的后台缓冲区。 管道 API 是处理大数据时的理想选择,但它是一个高级主题。
解决方案
我知道这远非最优化的解决方案,但在我的情况下它起到了作用,并且由于已知文件的纯文本部分相当小,因此不会导致任何明显的性能问题。这是代码:
using var fileStream = new FileStream(@"crg_sample.crg", FileMode.Open, FileAccess.Read);
using var reader = new BinaryReader(fileStream);
var newLine = '\n';
var markerString = "$$$$";
var currentString = "";
var foundMarker = false;
var foundNewLine = false;
while (!foundNewLine)
{
var c = reader.ReadChar();
if (!foundMarker)
{
currentString += c;
if (currentString.Length > markerString.Length)
currentString = currentString.Substring(1);
if (currentString == markerString)
foundMarker = true;
}
else
{
if (c == newLine)
foundNewLine = true;
}
}
if (foundNewLine)
{
// Read binary
}
注意: 如果您正在处理更大或更复杂的文件,您应该看看 Mark Gravell 的回答和评论区。
我正在尝试用 C# 解析 crg-file。该文件混合了纯文本和二进制数据。文件的第一部分包含纯文本,而文件的其余部分是二进制的(很多浮点数),这是一个示例:
$
$ROAD_CRG
reference_line_start_u = 100
reference_line_end_u = 120
$
$KD_DEFINITION
#:KRBI
U:reference line u,m,730.000,0.010
D:reference line phi,rad
D:long section 1,m
D:long section 2,m
D:long section 3,m
...
$
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
�@z����RA����\�l
...
我知道我可以读取从特定偏移量开始的字节,但我如何找出从哪个字节开始?二进制部分之前的最后一行将始终包含至少四个美元符号“$$$$”。到目前为止,这是我得到的:
using var fs = new FileStream(@"crg_sample.crg", FileMode.Open, FileAccess.Read);
var startByte = ??; // How to find out where to start?
using (BinaryReader reader = new BinaryReader(fs))
{
reader.BaseStream.Seek(startByte, SeekOrigin.Begin);
var f = reader.ReadSingle();
Debug.WriteLine(f);
}
更新:此代码可能无法按预期工作。请在评论中查看有价值的信息。
using (var fs = new FileStream(@"crg_sample.crg", FileMode.Open, FileAccess.Read))
{
using (StreamReader sr = new StreamReader(fs, Encoding.ASCII, true, 1, true))
{
var line = sr.ReadLine();
while (!string.IsNullOrWhiteSpace(line) && !line.Contains("$$$$"))
{
line = sr.ReadLine();
}
}
using (BinaryReader reader = new BinaryReader(fs))
{
// TODO: Start reading the binary data
}
}
当您混合使用文本数据和二进制数据时,您需要将所有内容都视为二进制数据。这意味着您应该使用原始 Stream
访问权限或类似的东西,并使用二进制 APIs 来查看文本数据(通常在 字节处寻找 cr/lf/crlf 作为哨兵,虽然在你的情况下听起来你可以只使用二进制 API 寻找 $$$$
,然后解码整个块,然后向前扫描)。当你认为你有一整行时,然后你可以使用 Encoding
来解析每一行 - 最方便的 API 是 encoding.GetString().
当你完成查看文本数据时 as binary, then 您可以继续解析 binary 数据,再次使用二进制 API。我也会 通常 在这里也反对 BinaryReader
,因为坦率地说,它并没有比更直接的 API 给你带来多少好处。您可能想考虑的 other 问题是 CPU 字节顺序,但假设这不是问题:BitConverter.ToSingle()
可能是您的朋友。
如果数据量不大,您可能会发现使用 byte[]
作为数据最简单;通过 File.ReadAllBytes
,或者从数组池中租用一个超大的 byte[]
,然后从 FileStream
加载它。 Stream
API 对于这种情况很尴尬,因为一旦你查看了数据:它就消失了——所以你需要维护自己的后台缓冲区。 管道 API 是处理大数据时的理想选择,但它是一个高级主题。
解决方案
我知道这远非最优化的解决方案,但在我的情况下它起到了作用,并且由于已知文件的纯文本部分相当小,因此不会导致任何明显的性能问题。这是代码:
using var fileStream = new FileStream(@"crg_sample.crg", FileMode.Open, FileAccess.Read);
using var reader = new BinaryReader(fileStream);
var newLine = '\n';
var markerString = "$$$$";
var currentString = "";
var foundMarker = false;
var foundNewLine = false;
while (!foundNewLine)
{
var c = reader.ReadChar();
if (!foundMarker)
{
currentString += c;
if (currentString.Length > markerString.Length)
currentString = currentString.Substring(1);
if (currentString == markerString)
foundMarker = true;
}
else
{
if (c == newLine)
foundNewLine = true;
}
}
if (foundNewLine)
{
// Read binary
}
注意: 如果您正在处理更大或更复杂的文件,您应该看看 Mark Gravell 的回答和评论区。