代码之家  ›  专栏  ›  技术社区  ›  hazzik

连接ReadOnlySpan<char>

  •  15
  • hazzik  · 技术社区  · 6 年前

    好啊NET Core 2.1已经登陆。有了它,我们有了一种处理字符串数据的新方法 ReadOnlySpan<char> .它非常擅长分割字符串数据,但是如何将跨度重新组合在一起呢?

    var hello = "Hello".AsSpan();
    var space = " ".AsSpan();
    var world = "World".AsSpan();
    
    var result = ...; // How do I get "Hello World" out of the 3 above?
    
    6 回复  |  直到 6 年前
        1
  •  10
  •   Dan Friedman    6 年前

    下面是一个例子,说明了。NET团队在内部处理这个问题 Path.Join :

    private static unsafe string JoinInternal(ReadOnlySpan<char> first, ReadOnlySpan<char> second)
    {
        Debug.Assert(first.Length > 0 && second.Length > 0, "should have dealt with empty paths");
    
        bool hasSeparator = PathInternal.IsDirectorySeparator(first[first.Length - 1])
            || PathInternal.IsDirectorySeparator(second[0]);
    
        fixed (char* f = &MemoryMarshal.GetReference(first), s = &MemoryMarshal.GetReference(second))
        {
            return string.Create(
                first.Length + second.Length + (hasSeparator ? 0 : 1),
                (First: (IntPtr)f, FirstLength: first.Length, Second: (IntPtr)s, SecondLength: second.Length, HasSeparator: hasSeparator),
                (destination, state) =>
                {
                    new Span<char>((char*)state.First, state.FirstLength).CopyTo(destination);
                    if (!state.HasSeparator)
                        destination[state.FirstLength] = PathInternal.DirectorySeparatorChar;
                    new Span<char>((char*)state.Second, state.SecondLength).CopyTo(destination.Slice(state.FirstLength + (state.HasSeparator ? 0 : 1)));
                });
        }
    }
    

    如果你想避免使用 unsafe 使用一些 大概 更容易阅读,您可以使用以下内容:

    public static ReadOnlySpan<char> Concat(this ReadOnlySpan<char> first, ReadOnlySpan<char> second)
    {
        return new string(first.ToArray().Concat(second.ToArray()).ToArray()).AsSpan();
    }
    
    public static ReadOnlySpan<char> Concat(this string first, ReadOnlySpan<char> second)
    {
        return new string(first.ToArray().Concat(second.ToArray()).ToArray()).ToArray();
    }
    

    使用 ReadOnlySpan 这是一个非常低的水平和优化的速度,所以你如何做到这一点可能取决于你自己的情况。但在很多情况下,回到过去可能没什么问题 string 插值和 StringBuilder (或者不要皈依 ReadOnlySpan 是的)。所以

    var sb = new StringBuilder();
    return sb
        .Append(hello)
        .Append(space)
        .Append(world)
        .ToString();
    

    return $"{hello.ToString()}{space.ToString()}{world.ToString()}";
    
        2
  •  7
  •   Steven Liekens    3 年前

    我认为值得一提的是,在中添加了用于连接跨距的重载。NET核心3以及对的支持。净核心2.1于2021 8月21日结束[ src ].如果您现在升级,那么您只需使用 一串连接两个字符串 .

    https://docs.microsoft.com/en-us/dotnet/api/system.string.concat?view=netcore-3.1#System_String_Concat_System_ReadOnlySpan_System_Char__System_ReadOnlySpan_System_Char__System_ReadOnlySpan_System_Char__

    var hello = "Hello".AsSpan();
    var space = " ".AsSpan();
    var world = "World".AsSpan();
    
    // .NET Core 3+
    var result = string.Concat(hello, space, world);
    
        3
  •  6
  •   Kalten    6 年前

    您可以使用这样的缓冲区来实现这一点=>

    var hello = "Hello".AsSpan();
    var space = " ".AsSpan();
    var world = "World".AsSpan();
    
    // First allocate the buffer with the target size
    char[] buffer = new char[hello.Length + space.Length + world.Length];
    // "Convert" it to writable Span<char>
    var span = new Span<char>(buffer);
    
    // Then copy each span at the right position in the buffer
    int index = 0;
    hello.CopyTo(span.Slice(index, hello.Length));
    index += hello.Length;
    
    space.CopyTo(span.Slice(index, space.Length));
    index += space.Length;
    
    world.CopyTo(span.Slice(index, world.Length));
    
    // Finality get back the string
    string result = span.ToString();
    

    通过使用arraypool重用缓冲区,可以再次对其进行优化

    char[] buffer =  ArrayPool<char>.Shared.Rent(hello.Length + space.Length + world.Length);
    // ...
    ArrayPool<char>.Shared.Return(buffer);
    
        4
  •  4
  •   Herman Schoenfeld    4 年前

    我编写并使用以下扩展/工具方法连接跨距:

       ReadOnlySpan<T> otherSpan = ...
       T[] someArray = ...
    
       var myArray = someSpan.Concat(otherSpan, someArray, etc);
       var myArray2 = SpanTool.Concat(someArray, otherSpan, etc); 
    

    斯潘。反恐精英

        public static class SpanExtensions {
    
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static T[] Concat<T>(this ReadOnlySpan<T> span0, ReadOnlySpan<T> span1)
                => SpanTool.Concat(span0, span1);
    
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static T[] Concat<T>(this ReadOnlySpan<T> span0, ReadOnlySpan<T> span1, ReadOnlySpan<T> span2)
                => SpanTool.Concat(span0, span1, span2);
    
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static T[] Concat<T>(this ReadOnlySpan<T> span0, ReadOnlySpan<T> span1, ReadOnlySpan<T> span2, ReadOnlySpan<T> span3)
                => SpanTool.Concat(span0, span1, span2, span3);
    
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public static T[] Concat<T>(this ReadOnlySpan<T> span0, ReadOnlySpan<T> span1, ReadOnlySpan<T> span2, ReadOnlySpan<T> span3, ReadOnlySpan<T> span4)
                => SpanTool.Concat(span0, span1, span2, span3, span4);
    
        }
    

    斯潘图尔。反恐精英

    
     public static class SpanTool {
    
            public static T[] Concat<T>(ReadOnlySpan<T> span0) {
                var result = new T[span0.Length];
                span0.CopyTo(result);
                return result;
            }
    
            public static T[] Concat<T>(ReadOnlySpan<T> span0, ReadOnlySpan<T> span1) {
                var result = new T[span0.Length + span1.Length];
                var resultSpan = result.AsSpan();
                span0.CopyTo(result);
                var from = span0.Length;
                span1.CopyTo(resultSpan.Slice(from));
                return result;
            }
    
            public static T[] Concat<T>(ReadOnlySpan<T> span0, ReadOnlySpan<T> span1, ReadOnlySpan<T> span2) {
                var result = new T[span0.Length + span1.Length + span2.Length];
                var resultSpan = result.AsSpan();
                span0.CopyTo(result);
                var from = span0.Length;
                span1.CopyTo(resultSpan.Slice(from));
                from += span1.Length;
                span2.CopyTo(resultSpan.Slice(from));
                return result;
            }
    
            public static T[] Concat<T>(ReadOnlySpan<T> span0, ReadOnlySpan<T> span1, ReadOnlySpan<T> span2, ReadOnlySpan<T> span3) {
                var result = new T[span0.Length + span1.Length + span2.Length + span3.Length];
                var resultSpan = result.AsSpan();
                span0.CopyTo(result);
                var from = span0.Length;
                span1.CopyTo(resultSpan.Slice(from));
                from += span1.Length;
                span2.CopyTo(resultSpan.Slice(from));
                from += span2.Length;
                span3.CopyTo(resultSpan.Slice(from));
                return result;
            }
    
            public static T[] Concat<T>(ReadOnlySpan<T> span0, ReadOnlySpan<T> span1, ReadOnlySpan<T> span2, ReadOnlySpan<T> span3, ReadOnlySpan<T> span4) {
                var result = new T[span0.Length + span1.Length + span2.Length + span3.Length + span4.Length];
                var resultSpan = result.AsSpan();
                span0.CopyTo(result);
                var from = span0.Length;
                span1.CopyTo(resultSpan.Slice(from));
                from += span1.Length;
                span2.CopyTo(resultSpan.Slice(from));
                from += span2.Length;
                span3.CopyTo(resultSpan.Slice(from));
                from += span3.Length;
                span4.CopyTo(resultSpan.Slice(from));
                return result;
            }
        }
    

    免责声明 在这里的答案不令人满意之后,我才为我的项目写了这篇文章。如果有任何错误,将在以后进行编辑。

        5
  •  3
  •   Serge    5 年前

    另一种选择是使用字符串。Concat,它接受ReadOnlySpan作为参数。下面是从github获得的实现

    internal static unsafe string Concat(ReadOnlySpan<char> str0, ReadOnlySpan<char> str1, ReadOnlySpan<char> str2)
    {
        var result = new string('\0', checked(str0.Length + str1.Length + str2.Length));
        fixed (char* resultPtr = result)
        {
            var resultSpan = new Span<char>(resultPtr, result.Length);
    
            str0.CopyTo(resultSpan);
            resultSpan = resultSpan.Slice(str0.Length);
    
            str1.CopyTo(resultSpan);
            resultSpan = resultSpan.Slice(str1.Length);
    
            str2.CopyTo(resultSpan);
        }
        return result;
    }
    

    https://github.com/dotnet/runtime/blob/4f9ae42d861fcb4be2fcd5d3d55d5f227d30e723/src/libraries/Microsoft.IO.Redist/src/Microsoft/IO/StringExtensions.cs

        6
  •  -1
  •   OzBob    6 年前

    我周五下午的快速思考:

    var hello = "Hello";
    var helloS = hello.AsSpan();
    var spaceS = " ".AsSpan();
    var worldS = "World".AsSpan();
    
    var sentence = helloS.ToString() + spaceS.ToString() + worldS.ToString();
    
    //Gives "Hello World"
    

    至少根据我在LinqPad上的快速游戏和对 System.Memory Source