This is probably really simple but I'm at the bottom of the learning curve with 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. 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.
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
:
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
. The use of Publish
is to make sure that all three of Buffer
, the opening selector, and the closing selector share the same subscription.
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. (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 }
Then finally use Select to get rid of the 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).
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
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);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.