将IObservable <byte []>拆分为字符,然后再划分为

时间:2015-07-03 13:48:02

标签: c# system.reactive

Rx很棒,但有时很难找到优雅的做事方式。这个想法很简单。我收到一个带有byte []的事件,这个数组可能包含一行,多行或一行。我想要的是找到一种方法来生成一个行的IObservable IObservable<String>,其中序列的每个元素都是一行。

几小时后,我发现最接近的解决方案非常难看,而且当然不能正常工作,因为扫描会在每个字符上触发OnNext:

//Intermediate subject use to transform byte[] into char
var outputStream = new Subject<char>();
_reactiveSubcription = outputStream
    //Scan doesn't work it trigger OnNext on every char
    //Aggregate doesn't work neither as it doesn't return intermediate result
    .Scan(new StringBuilder(), (builder, c) => c == '\r' ? new StringBuilder() : builder.Append((char)c))
    .Subscribe(this);


Observable.FromEventPattern<ShellDataEventArgs>(shell, "DataReceived")
            //Data is a byte[]
            .Select(_ => _.EventArgs.Data)
            .Subscribe(array => array.ToObservable()
            //Convert into char
            .ForEach(c => outputStream.OnNext((char)c)));

注意:_reactiveSubcription应该是IObservable<String>

在不考虑字符编码问题的情况下,我缺少什么才能使其发挥作用?

1 个答案:

答案 0 :(得分:6)

这适合我。

首先,将byte []转换为字符串并在\r上拆分字符串(Regex Split保留分隔符)。

现在有一个字符串流,其中一些以\r结尾。

然后Concat,让他们保持秩序。此外,由于strings需要热门才能进行下一步,因此请将其发布。

var strings = bytes.
  Select(arr => (Regex.Split(Encoding.Default.GetString(arr, 0, arr.Length - 1), "(\r)")).
    Where(s=> s.Length != 0).
    ToObservable()).
  Concat().
  Publish().
  RefCount();

创建一个字符串窗口,当字符串以\r结尾时结束。 strings需要很热,因为它用于Window内容和窗口结束触发器。

var linewindows = strings.Window(strings.Where(s => s.EndsWith("\r")));

将每个窗口聚合为一个字符串。

var lines = linewindows.SelectMany(w => w.Aggregate((l, r) => l + r));

linesIObservable<String>,每个字符串包含一行。

为了测试这个,我使用以下生成器来生成IObservable<byte[]>

var bytes = Observable.
Range(1, 10).
SelectMany(i => Observable.
    Return((byte)('A' + i)).
    Repeat(24).
    Concat(Observable.
        Return((byte)'\r'))).
Window(17).
SelectMany(w => w.ToArray());