内联解析IObservable <byte> </byte>

时间:2014-05-07 12:55:54

标签: system.reactive

我有一个可观察的查询,它从我想要内联解析的流中生成IObservable<byte>。我希望能够根据数据源使用不同的策略来解析来自此序列的离散消息。请记住,我仍处于RX的向上学习曲线上。我已经提出了一个解决方案,但我不确定是否有办法使用开箱即用的操作员来实现这一目标。

首先,我将以下扩展方法写入IObservable:

    public static IObservable<IList<T>> Parse<T>(
        this IObservable<T> source,
        Func<IObservable<T>, IObservable<IList<T>>> parsingFunction)
    {
        return parsingFunction(source);
    }

这允许我指定特定数据源使用的消息成帧策略。一个数据源可能由一个或多个字节分隔,而另一个数据源可能由开始和停止块模式分隔,而另一个可能使用长度前缀策略。所以这是我定义的定界策略的一个例子:

public static class MessageParsingFunctions
{

    public static Func<IObservable<T>, IObservable<IList<T>>> Delimited<T>(T[] delimiter)
    {
        if (delimiter == null) throw new ArgumentNullException("delimiter");
        if (delimiter.Length < 1) throw new ArgumentException("delimiter must contain at least one element.");

        Func<IObservable<T>, IObservable<IList<T>>> parser =
            (source) =>
            {
                var shared = source.Publish().RefCount();

                var windowOpen = shared.Buffer(delimiter.Length, 1)
                    .Where(buffer => buffer.SequenceEqual(delimiter))
                    .Publish()
                    .RefCount();

                return shared.Buffer(windowOpen)
                    .Select(bytes =>
                        bytes
                        .Take(bytes.Count - delimiter.Length)
                        .ToList());

            };

        return parser;
    }
}

最后,作为一个例子,我可以使用以下方式的代码在序列中遇到字符串'<EOF>'的字节模式时解析序列中的离散消息:

var messages = ...operators that surface an IObservable<byte>
    .Parse(MessageParsingFunctions.Delimited(Encoding.ASCII.GetBytes("<EOF>")))
    ...further operators to package discrete messages along with additional metadata

问题:

  1. 使用开箱即用的操作员是否有更直接的方法来实现这一目标?
  2. 如果不是,最好只将不同的解析函数(即ParseDelimited,ParseLengthPrefixed等)定义为本地扩展,而不是使用接受解析函数的更通用的Parse扩展方法?
  3. 提前致谢!

1 个答案:

答案 0 :(得分:0)

看看Rxx Parsers。这是一个related lab。例如:

IObservable<byte> bytes = ...;

var parsed = bytes.ParseBinary(parser =>
  from next in parser
  let magicNumber = parser.String(Encoding.UTF8, 3).Where(value => value == "RXX")
  let header = from headerLength in parser.Int32
               from header in next.Exactly(headerLength)
               from headerAsString in header.Aggregate(string.Empty, (s, b) => s + " " + b)
               select headerAsString
  let message = parser.String(Encoding.UTF8)
  let entry = from length in parser.Int32
              from data in next.Exactly(length)
              from value in data.Aggregate(string.Empty, (s, b) => s + " " + b)
              select value
  let entries = from count in parser.Int32
                from entries in entry.Exactly(count).ToList()
                select entries
  select from _ in magicNumber.Required("The file's magic number is invalid.")
         from h in header.Required("The file's header is invalid.")
         from m in message.Required("The file's message is invalid.")
         from e in entries.Required("The file's data is invalid.")
         select new
         {
           Header = h,
           Message = m,
           Entries = e.Aggregate(string.Empty, (acc, cur) => acc + cur + Environment.NewLine)
         });