C# 解析表示';固定长度';具有不同大小字段的消息

C# 解析表示';固定长度';具有不同大小字段的消息,c#,string-parsing,edi,fixed-length-record,C#,String Parsing,Edi,Fixed Length Record,我在中有一个固定长度的字符串消息,如下所示: "\0\0\0j\0\0\0\vT3A1111 2999BOSH 2100021 399APV 2100022 " void Main() { var line = "00580011T3A1111 2999Bosh 399APV

我在中有一个固定长度的字符串消息,如下所示:

"\0\0\0j\0\0\0\vT3A1111        2999BOSH                          2100021        399APV                           2100022  "
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; }
}
此消息是由我将字节[]读入StringBuilder以生成字符串创建的

上面,字符串部分“
\0\0\0j\0\0\0\v
”应该是长度和ID字段,两个字段都有4个字节长。但是,我不确定如何提取这两个值,但我可以看到,
HEX 0j
是106(1+1+8+9+30+9+9+30+9+30+9总长度=106)。我不确定上面的“v”为什么不是“0v”,但我知道它应该是表示消息id的十六进制值

长度为4的前两个字段为十六进制,所有其他字段为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个制造商制造的零件,但在实际示例中,可能不止2个零件:

Part 1, 29.99, made by Bosh, part# 2100021
Part 2, 3.99, made by APV, part# 2100022
我想将这个平面文件字符串中的所有price和manufacturer字段放入一个列表对象中,其中包含零件

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");
    }
}
@神秘性-我试着发布你的答案并在LinqPad中运行它(从未使用过,只是下载并安装了它),但我看不到你在答案中发布的类似表格的结构。你怎么知道的

这是我得到的

您说“byte[]进入StringBuilder以构建字符串”,所以我认为您有一个字符串。也许可以尝试使用子字符串(…),类似于:

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

编辑:如果有不需要的填充字符,请尝试

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

虽然不雅致,但它会起作用。

或许可以尝试以下方式:

"\0\0\0j\0\0\0\vT3A1111        2999BOSH                          2100021        399APV                           2100022  "
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; }
}
对于我使用的示例数据(我必须修复它,因为即使在我删除
|
并用空格替换
-
时,它在您的问题中似乎已损坏),我得到以下结果:


我无法从您的措辞判断字段长度是否可变,以及它们是否在消息中使用类似“44118930930930”的字符串进行编码。如果是,您如何解析它?您如何知道第一个字段是4字节而不是44字节?假设字段是固定长度的,我使用了
TextFieldParser
类(在
Microsoft.VisualBasic
程序集中-但为了澄清我的代码是
c
)从中读取固定宽度的数据files@nlawalker我更新了问题,请看开头。很抱歉。Brendan,我想避免使用VB库,我已经看过了,这样的东西怎么样。不确定引用Microsoft.VisualBasic.dll与引用Nuget软件包有什么区别,但两者都应该得到相同的结果outcome@cd491415-您是否有
字符串
(这是一个
字符序列
)或
字节[]
(这是一个
字节序列
)?它们是不同的。是的,这就是我所说的手工。我想知道是否有一种使用开源库的简单方法。或者正则表达式之类的。。。谢谢,非常感谢,我来看看这个。:)我认为这也是我学习的一个机会,如果您可以添加一些解释,说明如何在上面的代码中获取变量的开头、字段和部分,那就太好了。而且,我以前从未见过像C#那样创建带有字段和值的消息。美好的这将帮助我更好地理解并再次感谢:)@cd491415-LINQPad是我用来创建对象图形表示的工具。它是任何.NET开发人员都必须拥有的工具,IMHO。@cd491415-我很乐意帮助您提供所需的详细信息,但我已要求您提供C#格式的纯数据,以便在您的问题中提供更好的详细信息,而您还没有这样做。很简单。只需给我们几行代码,比如
var messages=new[]{“00580011T3A1111 2999Bosh 399APV 2399MAG”,}。很简单。在TCP/IP通信中,我以字节[]数组的形式将这些数据转换为字符串。因此,我没有这些数据的C#代码,也没有这些数据是如何生成和发送给我的。我收到的数据只有字符串表示,我已经在上面提供了,除非我遗漏了什么?