Разбор строки C #, представляющей сообщение «фиксированной длины» с полями разных размеров - PullRequest
0 голосов
/ 16 февраля 2019

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

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

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

Выше, строковая часть "\0\0\0j\0\0\0\v" должна быть длиной и полями ID, оба длиной 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 (никогда не использовал его, просто скачал и установил его), но я не вижу табличную структуру, которую вы разместили в своемответ.Как ты это понял?

Вот что я получаю enter image description here

Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

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

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

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

message

0 голосов
/ 16 февраля 2019

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

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

и т. Д.

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...