简体   繁体   English

学习Rx:如何将可观察的字符序列解析为可观察的字符串序列?

[英]Learning Rx: How can I parse an observable sequence of characters into an observable sequence of strings?

This is probably really simple but I'm at the bottom of the learning curve with Rx. 这可能非常简单,但我在Rx的学习曲线底部。 I've spent several hours reading articles, watching videos and writing code but I seem to have a mental block on something that seems like it should be really simple. 我花了几个小时阅读文章,观看视频和编写代码,但我似乎对一些似乎应该非常简单的事情有一些心理障碍。

I'm gathering data from a serial port. 我正在从串口收集数据。 I have used Observable.FromEventPattern to capture the SerialDataReceived event and convert it to an observable sequence of characters. 我使用Observable.FromEventPattern捕获SerialDataReceived事件并将其转换为可观察的字符序列。 So far so good. 到现在为止还挺好。

Now, I want to parse that character sequence based on separator characters. 现在,我想基于分隔符来解析该字符序列。 There are no newlines involved, but each 'packet' of data is surrounded by a preamble and a terminator, both single characters. 没有涉及新行,但每个“数据包”数据都由前导码和终结符包围,两个单个字符。 For the sake of argument, lets say they are braces { and } . 为了论证,我们可以说它们是大括号{}

So if I get the character sequence j u n k { H e l l o } j u n k on my character sequence, then I want to emit either Hello or {Hello} on my string sequence. 所以,如果我得到的字符序列j u n k { H e l l o } j u n k在我的字符序列,那么我想发出任何Hello{Hello}我的字符串序列。

I'm probably missing something simple but I can't even begin to figure out how to approach this. 我可能错过了一些简单的东西,但我甚至无法弄清楚如何处理这个问题。 Any suggestions please? 有什么建议吗?

This can be easily accomplished using Publish and Buffer : 使用PublishBuffer可以轻松完成此操作:

var source = "junk{Hello}junk{World}junk".ToObservable();
var messages = source
    .Publish(o =>
    {
        return o.Buffer(
            o.Where(c => c == '{'),
            _ => o.Where(c => c == '}'));
    })
    .Select(buffer => new string(buffer.ToArray()));
messages.Subscribe(x => Console.WriteLine(x));
Console.ReadLine();

The output of this is: 这个输出是:

{Hello}
{World}

The idea is that you can use the following opening and closing selectors in the call to Buffer . 我们的想法是,您可以在调用Buffer使用以下打开和关闭选择器。 The use of Publish is to make sure that all three of Buffer , the opening selector, and the closing selector share the same subscription. 使用Publish是为了确保所有三个Buffer ,开放选择器和结束选择器共享相同的订阅。

source:  junk{Hello}junk{World}junk|
opening: ----{----------{----------|
closing:     ------}|
closing:                ------}|

Use Scan to aggregate your so-far-received values into the aggregated string ( TAccumulate is a string ), and reset that string to "" every time you get an end brace. 使用“ 扫描”将您的远程接收值聚合到聚合字符串中( TAccumulate是一个string ),并在每次获得结束括号时将该字符串重置为"" (I'll leave the work of implementing the aggregation function up to you). (我将把实现聚合功能的工作留给你)。 This will produce observables like 这会产生类似的观察

j
ju
jun
junk
junk{
junk{h
junk{hi
junk{hi}
j
ju
...

Then you can use Where to only emit the ones that end with } 然后你可以使用Where只发出结尾的那些}

Then finally use Select to get rid of the junk . 然后最后使用Select来摆脱junk

So in full, should be 所以完整,应该是

IObservable<string> packetReceived = 
  serialPort.CharReceived
    .Scan(YourAggregationFunction)
    .Where(s => s.EndsWith("}"))
    .Select(s => s.EverythingAfter("{"));

(I leave EverythingAfter up to you to implement as well). (我将EverythingAfter留给你实施)。

Just a note, as you're experimenting with the aggregation function, it may be easier to use the IEnumerable interface of string to test it, ie 只是注意,当您正在尝试聚合函数时,使用stringIEnumerable接口来测试它可能更容易,即

foreach (s in "junk{hi}hunk{ji}blah".Scan(YourAggregationFunction))
  Console.WriteLine(s);

Okay, here's a full working example 好的,这是一个完整的工作示例

static void Main(string[] args) {
    var stuff = "junk{hi}junk{world}junk".ToObservable()
        .Scan("", (agg, c) => agg.EndsWith("}") ? c.ToString() : agg + c)
        .Where(s => s.EndsWith("}"))
        .Select(s => s.Substring(s.IndexOf('{')));
    foreach (var thing in stuff.ToEnumerable()) {
        Console.WriteLine(thing);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM