Разбор строки C#, представляющей сообщение "фиксированной длины" с полями разных размеров

У меня есть строковое сообщение фиксированной длины в, которое выглядит так:

"\0\0\0j\0\0\0\vT3A1111        2999BOSH                          2100021        399APV                           2100022  "

Это сообщение создано из моего чтения байта [] в StringBuilder для построения строки.

Выше, строковая часть " \0\0\0j\0\0\0\v "Предполагается, что это поля длины и идентификатора, длиной 4 байта. Однако я не уверен, как извлечь эти 2 значения, но я вижу, что HEX 0j составляет 106 (1+1+8+9+30+9+9+30+9= всего 106 в длину). Я не уверен, почему "v" не равно "0v" выше, но я знаю, что это должно быть значение HEX, представляющее идентификатор сообщения.

Первые 2 поля длины 4 - HEX, все остальные - ASCII.

Это не EDI-сообщение (поэтому нельзя использовать библиотеку EDI-анализатора), и в отличие от EDI-сообщений, которые имеют некоторый идентификатор поля, у меня есть только поток байтов, и я знаю только длину полей. Поля:

4  byte long message length      ("\0\0\0j")
4  byte long message id          ("\0\0\0\v")
1  byte long message type        ("T")
1  byte long message sequence    ("3")
8  byte long car Id              ("A1111   ")  
9  byte long part-1 price        ("     2999")
30 byte long part-1 manufacturer ("BOSH                          ")
9  byte long part#               ("2100021  ")
9  byte long part-2 price        ("      399")
30 byte long part-2 manufacturer ("APV                           ")
9  byte long part#               ("2100022  ")

Итак, выше у меня есть 2 детали, сделанные двумя производителями, но в реальном примере это может быть больше, чем просто 2 детали:

Part 1, 29.99, made by Bosh, part# 2100021
Part 2, 3.99, made by APV, part# 2100022

Я хотел бы получить все поля цены и производителя из этой строки плоского файла в объекты List, где Part

class Part
{
   public decimal Price {get; set}
   public string Manufacturer {get; set;}
   public string PartNumber {get; set;}
}

Итак, мой список будет содержать все запчасти с указанием их цен и производителей.

Поскольку у меня есть длины каждого поля, я знаю, что мог бы перебрать эту строку и получить данные, связанные с деталью. Но мне интересно, есть ли более элегантный и простой способ сделать это.

Или даже лучше, есть ли библиотека с открытым исходным кодом, позволяющая мне анализировать что-то вроде этого?

Я получаю это сообщение, используя этот метод

private TcpClient clientSocket;
private NetworkStream serverStream;

private async System.Threading.Tasks.Task ReadResponseAsync()
{
    if (serverStream.CanRead)
    {
        byte[] readBuffer = new byte[1024];
        StringBuilder receivedMessage = new StringBuilder();
        int readSoFar = 0;

        do
        {
            readSoFar = await serverStream.ReadAsync(readBuffer, 0, readBuffer.Length);
            receivedMessage.AppendFormat("{0}", Encoding.ASCII.GetString(readBuffer, 0, readSoFar));
        } 
        while (serverStream.DataAvailable);

        string msg = receivedMessage.ToString();
    }
    else
    {
        Log("Error", "Cannot read from NetworkStream");
    }
}

@Enigmativity - я попытался опубликовать ваш ответ и запустить его в LinqPad (никогда не использовал его, просто скачал и установил), но я не вижу табличную структуру, которую вы разместили в своем ответе. Как ты это понял?

Вот что я получаю

2 ответа

Решение

Вы говорите "byte[] в StringBuilder для построения строки", поэтому я так понимаю, у вас есть строка. Возможно, попробуйте использовать SubString(..), что-то вроде:

var length = int.Parse(message.SubString(0,4);
var id = int.Parse(message.SubString(4,4);

так далее

Редактировать: если есть нежелательные символы-заполнители, попробуйте

message.Replace('-', ' ');

Не элегантно, но это будет работать.

Возможно, попробуйте что-то вроде этого:

void Main()
{
    var line = "00580011T3A1111        2999Bosh                                399APV                                2399MAG                           ";

    var lengths = new[] { 4, 4, 1, 1, 8, 9, 30, 9, 30, 9, 30 };
    var starts = lengths.Aggregate(new[] { 0 }.ToList(), (a, x) => { a.Add(a.Last() + x); return a; });

    var fields = starts.Zip(lengths, (p, l) => line.Substring(p, l).Trim()).ToArray();

    var message = new
    {
        message_length = int.Parse(fields[0]),
        message_id = int.Parse(fields[1]),
        message_type = fields[2],
        message_sequence = int.Parse(fields[3]),
        car_Id = fields[4],
        parts =
            Enumerable
                .Range(0, 3)
                .Select(x => x * 2 + 5)
                .Select(x => new Part
                {
                    Price = decimal.Parse(fields[x]),
                    Manufacturer = fields[x + 1]
                }).ToArray(),
    };
}

public class Part
{
    public decimal Price { get; set; }
    public string Manufacturer { get; set; }
}

На примере данных, которые я использовал (которые я должен был исправить, так как он кажется поврежденным в вашем вопросе, даже когда я удаляю | и заменить - с пробелами), я получаю этот результат:

ht tps://stackru.com/image s/664ee8ad5f2321a16601040254067010aaa3cab6.png

Другие вопросы по тегам