C# - чтение байтов из файла из указанной строки c - PullRequest
3 голосов
/ 20 января 2020

Я пытаюсь проанализировать crg-файл в C#. Файл смешан с простым текстом и двоичными данными. Первый раздел файла содержит простой текст, в то время как остальная часть файла является двоичной (много чисел с плавающей запятой), вот пример:

$
$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
...

Я знаю, что могу читать байты, начиная со определенного смещения c но как мне узнать, с какого байта начинать? Последняя строка перед двоичным разделом всегда будет содержать как минимум четыре знака доллара "$$$$". Вот что у меня так далеко:

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);
}

Ответы [ 3 ]

3 голосов
/ 20 января 2020

Когда у вас есть смесь текстовых данных и двоичных данных, вы должны рассматривать все как двоичные. Это означает, что вы должны использовать необработанный Stream доступ или что-то подобное и использовать двоичные API-интерфейсы для просмотра текстовых данных (часто ища cr / lf / crlf для байт в качестве часовых, хотя это звучит как в вашем случае вы можете просто найти $$$$, используя двоичные API, затем декодировать весь блок и сканировать вперед). Когда вы думаете, что у вас есть целая строка, вы можете использовать Encoding для разбора каждой строки - наиболее удобным API является encoding.GetString(). Когда вы закончите просматривать текстовые данные как двоичные , затем вы можете продолжить анализ двоичных данных , снова используя двоичный API. Я бы обычно рекомендовал бы против BinaryReader и здесь, потому что, честно говоря, это не дает вам намного больше, чем более прямой API. Другая проблема , о которой вы, возможно, захотите подумать, - это постоянство ЦП, но при условии, что это не проблема: BitConverter.ToSingle() может быть вашим другом.

Если данные скромного размера, вам может быть проще использовать byte[] для данных; либо через File.ReadAllBytes, либо взяв в аренду негабаритный byte[] из пула массивов, и загрузив его из FileStream. API Stream неудобен для такого рода сценариев, потому что как только вы посмотрите на данные: они исчезли - поэтому вам нужно поддерживать свои собственные обратные буферы. API конвейеров идеально подходит для этого, когда имеешь дело с большими данными, но является продвинутой версией c.

0 голосов
/ 21 января 2020

Решение

Я знаю, что это далеко не самое оптимизированное решение, но в моем случае это сработало, и, поскольку было известно, что раздел с открытым текстом файла довольно мал, не вызывает каких-либо заметных проблем с производительностью. Вот код:

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 и разделы комментариев.

0 голосов
/ 20 января 2020

ОБНОВЛЕНИЕ: Этот код может работать некорректно. Пожалуйста, ознакомьтесь с ценной информацией в комментариях.

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
    }
}
...