Разбор строки 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