有类似 Buffer.Last Position Of 的东西吗?查找缓冲区中最后一次出现的字符?

2024-02-28

我有一个类型的缓冲区ReadOnlySequence<byte>。我想从中提取一个子序列(其中将包含 0 - n 条消息),方法是知道每条消息以0x1c, 0x0d(如所描述的here http://healthstandards.com/blog/2007/05/02/hl7-mlp-minimum-layer-protocol-defined/).

我知道缓冲区有一个扩展方法位置 https://learn.microsoft.com/en-us/dotnet/api/system.buffers.buffersextensions.positionof?view=netcore-3.1 but it

返回第一次出现的位置item in the ReadOnlySequence<T>.

我正在寻找一种方法,该方法可以返回最后一次出现的位置。我尝试自己实现它,这就是我到目前为止所拥有的

private SequencePosition? GetLastPosition(ReadOnlySequence<byte> buffer)
{
    // Do not modify the real buffer
    ReadOnlySequence<byte> temporaryBuffer = buffer;
    SequencePosition? lastPosition = null;

    do
    {
        /*
            Find the first occurence of the delimiters in the buffer
            This only takes a byte, what to do with the delimiters? { 0x1c, 0x0d }

        */
        SequencePosition? foundPosition = temporaryBuffer.PositionOf(???);

        // Is there still an occurence?
        if (foundPosition != null)
        {
            lastPosition = foundPosition;

            // cut off the sequence for the next run
            temporaryBuffer = temporaryBuffer.Slice(0, lastPosition.Value);
        }
        else
        {
            // this is required because otherwise this loop is infinite if lastPosition was set once
            break;
        }
    } while (lastPosition != null);

    return lastPosition;
}

我正在为此挣扎。首先PositionOf方法只需要一个byte但有两个分隔符,所以我必须传递一个byte[]。接下来我想我可以“以某种方式”优化循环。

您知道如何找到这些分隔符的最后一次出现吗?


我深入研究了这个问题,但我设法想出了一个扩展方法,我认为它回答了你的问题:

using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;

public static class ReadOnlySequenceExtensions
{
    public static SequencePosition? LastPositionOf(
        this ReadOnlySequence<byte> source,
        byte[] delimiter)
    {
        if (delimiter == null)
        {
            throw new ArgumentNullException(nameof(delimiter));
        }
        if (!delimiter.Any())
        {
            throw new ArgumentException($"{nameof(delimiter)} is empty", nameof(delimiter));
        }

        var reader = new SequenceReader<byte>(source);
        var delimiterToFind = new ReadOnlySpan<byte>(delimiter);

        var delimiterFound = false;
        // Keep reading until we've consumed all delimiters
        while (reader.TryReadTo(out _, delimiterToFind, true))
        {
            delimiterFound = true;
        }

        if (!delimiterFound)
        {
            return null;
        }

        // If we got this far, we've consumed bytes up to,
        // and including, the last byte of the delimiter,
        // so we can use that to get the position of 
        // the starting byte of the delimiter
        return reader.Sequence.GetPosition(reader.Consumed - delimiter.Length);
    }
}

这里还有一些测试用例:

var cases = new List<byte[]>
{
    // Case 1: Check an empty array
    new byte[0],
    // Case 2: Check an array with no delimiter
    new byte[] { 0xf },
    // Case 3: Check an array with part of the delimiter
    new byte[] { 0x1c },
    // Case 4: Check an array with the other part of the delimiter
    new byte[] { 0x0d },
    // Case 5: Check an array with the delimiter in the wrong order
    new byte[] { 0x0d, 0x1c },
    // Case 6: Check an array with a correct delimiter
    new byte[] { 0x1c, 0x0d },
    // Case 7: Check an array with a byte followed by a correct delimiter
    new byte[] { 0x1, 0x1c, 0x0d },
    // Case 8: Check an array with multiple correct delimiters
    new byte[] { 0x1, 0x1c, 0x0d, 0x2, 0x1c, 0x0d },
    // Case 9: Check an array with multiple correct delimiters
    // where the delimiter isn't the last byte
    new byte[] { 0x1, 0x1c, 0x0d, 0x2, 0x1c, 0x0d, 0x3 },
    // Case 10: Check an array with multiple sequential bytes of a delimiter
    new byte[] { 0x1, 0x1c, 0x0d, 0x2, 0x1c, 0x1c, 0x0d, 0x3 },
};

var delimiter = new byte[] { 0x1c, 0x0d };
foreach (var item in cases)
{
    var source = new ReadOnlySequence<byte>(item);
    var result = source.LastPositionOf(delimiter);
} // Put a breakpoint here and examine result

Cases 1 to 5全部正确返回null. Cases 6 to 10全部正确返回SequencePosition到分隔符中的第一个字节(即在这种情况下,0x1c).

我还尝试创建一个迭代版本,在找到分隔符后会产生一个位置,如下所示:

while (reader.TryReadTo(out _, delimiterToFind, true))
{
    yield return reader.Sequence.GetPosition(reader.Consumed - delimiter.Length);
}

But SequenceReader<T> and ReadOnlySpan<T>不能在迭代器块中使用,所以我想出了AllPositionsOf反而:

public static IEnumerable<SequencePosition> AllPositionsOf(
    this ReadOnlySequence<byte> source,
    byte[] delimiter)
{
    if (delimiter == null)
    {
        throw new ArgumentNullException(nameof(delimiter));
    }
    if (!delimiter.Any())
    {
        throw new ArgumentException($"{nameof(delimiter)} is empty", nameof(delimiter));
    }

    var reader = new SequenceReader<byte>(source);
    var delimiterToFind = new ReadOnlySpan<byte>(delimiter);

    var results = new List<SequencePosition>();
    while (reader.TryReadTo(out _, delimiterToFind, true))
    {
        results.Add(reader.Sequence.GetPosition(reader.Consumed - delimiter.Length));
    }

    return results;
}

测试用例也能正常工作。

Update

现在我已经睡了一些觉,并且有机会思考一些事情,我认为上述问题可以改进,原因如下:

  1. SequenceReader<T> has a Rewind()方法,这让我思考SequenceReader<T>被设计为可重复使用
  2. SequenceReader<T>似乎是为了让它更容易使用而设计的ReadOnlySequence<T>一般而言
  3. 创建扩展方法ReadOnlySequence<T>为了使用SequenceReader<T>读取ReadOnlySequence<T>似乎倒退

鉴于上述情况,我认为尝试避免直接与ReadOnlySequence<T>在可能的情况下,优先选择并重复使用,SequenceReader<T>反而。考虑到这一点,这是一个不同的版本LastPositionOf现在是一个扩展方法SequenceReader<T>:

public static class SequenceReaderExtensions
{
    /// <summary>
    /// Finds the last occurrence of a delimiter in a given sequence.
    /// </summary>
    /// <param name="reader">The reader to read from.</param>
    /// <param name="delimiter">The delimeter to look for.</param>
    /// <param name="rewind">If true, rewinds the reader to its position prior to this method being called.</param>
    /// <returns>A SequencePosition if a delimiter is found, otherwise null.</returns>
    public static SequencePosition? LastPositionOf(
        this ref SequenceReader<byte> reader,
        byte[] delimiter,
        bool rewind)
    {
        if (delimiter == null)
        {
            throw new ArgumentNullException(nameof(delimiter));
        }
        if (!delimiter.Any())
        {
            throw new ArgumentException($"{nameof(delimiter)} is empty", nameof(delimiter));
        }

        var delimiterToFind = new ReadOnlySpan<byte>(delimiter);
        var consumed = reader.Consumed;

        var delimiterFound = false;
        // Keep reading until we've consumed all delimiters
        while (reader.TryReadTo(out _, delimiterToFind, true))
        {
            delimiterFound = true;
        }

        if (!delimiterFound)
        {
            if (rewind)
            {
                reader.Rewind(reader.Consumed - consumed);
            }

            return null;
        }

        // If we got this far, we've consumed bytes up to,
        // and including, the last byte of the delimiter,
        // so we can use that to get the starting byte
        // of the delimiter
        var result = reader.Sequence.GetPosition(reader.Consumed - delimiter.Length);
        if (rewind)
        {
            reader.Rewind(reader.Consumed - consumed);
        }

        return result;
    }
}

上面的测试用例继续通过,但我们现在可以重用相同的reader。此外,它还允许您指定是否要倒带到原始位置reader在被调用之前。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有类似 Buffer.Last Position Of 的东西吗?查找缓冲区中最后一次出现的字符? 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • C# 异步等待澄清?

    我读了here http blog stephencleary com 2012 02 async and await html that 等待检查等待的看看它是否有already完全的 如果 可等待已经完成 那么该方法将继续 运行 同步
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • free 和 malloc 在 C 中如何工作?

    我试图弄清楚如果我尝试 从中间 释放指针会发生什么 例如 看下面的代码 char ptr char malloc 10 sizeof char for char i 0 i lt 10 i ptr i i 10 ptr ptr ptr pt
  • C++ 多行字符串原始文字[重复]

    这个问题在这里已经有答案了 我们可以像这样定义一个多行字符串 const char text1 part 1 part 2 part 3 part 4 const char text2 part 1 part 2 part 3 part 4
  • 在 Unity 中实现 Fur with Shells 技术

    我正在尝试在 Unity 中实现皮毛贝壳技术 http developer download nvidia com SDK 10 5 direct3d Source Fur doc FurShellsAndFins pdf Fins 技术被
  • 使用 C# 中的 CsvHelper 将不同文化的 csv 解析为十进制

    C 中 CsvHelper 解析小数的问题 我创建了一个从 byte 而不是文件获取 csv 文件的类 并且它工作正常 public static List
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • 实例化类时重写虚拟方法

    我有一个带有一些虚函数的类 让我们假设这是其中之一 public class AClassWhatever protected virtual string DoAThingToAString string inputString retu
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 为什么使用小于 32 位的整数?

    我总是喜欢使用最小尺寸的变量 这样效果就很好 但是如果我使用短字节整数而不是整数 并且内存是 32 位字可寻址 这真的会给我带来好处吗 编译器是否会做一些事情来增强内存使用 对于局部变量 它可能没有多大意义 但是在具有数千甚至数百万项的结构
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 如何在 Linq to SQL 中使用distinct 和 group by

    我正在尝试将以下 sql 转换为 Linq 2 SQL select groupId count distinct userId from processroundissueinstance group by groupId 这是我的代码
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置

随机推荐