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 的回答和评论区。