Tranches de tableau en C#

Comment faites-vous? Donné un tableau d'octets:

byte[] foo = new byte[4096];

Comment pourrais-je obtenir les premiers octets x du tableau comme un tableau séparé? (Spécifiquement, j'en ai besoin comme un IEnumerable<byte> )

Ceci est pour travailler avec Socket s. Je pense que la manière la plus facile serait de découper des tableaux, similaire à la syntaxe Perls:

@bar = @foo[0..40];

qui renverrait les 41 premiers éléments dans le tableau @bar . Y a-t-il quelque chose en C que je suis manque juste, ou est-il autre chose que je devrais faire?

LINQ est une option pour moi (.NET 3.5), si cela peut aider.

183
demandé sur Jeff Yates 2009-01-02 13:49:05

16 réponses

Les matrices

sont dénombrables, donc votre foo est déjà un IEnumerable<byte> lui-même. Utilisez simplement les méthodes de la séquence LINQ comme Take() pour obtenir ce que vous voulez (n'oubliez pas d'inclure l'espace de noms Linq avec using System.Linq; ):

byte[] foo = new byte[4096];

var bar = foo.Take(41);

si vous avez vraiment besoin d'un tableau à partir de n'importe quelle valeur IEnumerable<byte> , vous pouvez utiliser la méthode ToArray() pour cela. Ce qui ne semble pas être le cas ici.

158
répondu peSHIr 2016-07-17 09:50:24

vous pourriez utiliser ArraySegment<T> . C'est très léger car il ne copie pas le tableau:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );
176
répondu Mike Scott 2009-01-02 12:29:41

vous pouvez utiliser la méthode des tableaux CopyTo() .

ou avec LINQ vous pouvez utiliser Skip() et Take() ...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);
104
répondu Arjan Einbu 2016-07-01 13:48:40
static byte[] SliceMe(byte[] source, int length)
{
    byte[] destfoo = new byte[length];
    Array.Copy(source, 0, destfoo, 0, length);
    return destfoo;
}

/ /

var myslice = SliceMe(sourcearray,41);
50
répondu WOPR 2011-05-13 05:45:57

une autre possibilité que je n'ai pas vu mentionnée ici: tampon.BlockCopy () est légèrement plus rapide que Array.Copier(), et il a l'avantage supplémentaire d'être en mesure de convertir à la volée à partir d'un tableau de primitives (par exemple, la court[]) pour un tableau d'octets, ce qui peut être pratique quand vous avez des tableaux numériques que vous avez besoin de transmettre sur les Sockets.

16
répondu Ken Smith 2010-09-23 23:31:32

Voici une méthode d'extension simple qui retourne une tranche comme un nouveau tableau:

public static T[] Slice<T>(this T[] arr, uint indexFrom, uint indexTo) {
    if (indexFrom > indexTo) {
        throw new ArgumentOutOfRangeException("indexFrom is bigger than indexTo!");
    }

    uint length = indexTo - indexFrom;
    T[] result = new T[length];
    Array.Copy(arr, indexFrom, result, 0, length);

    return result;
}

, Alors vous pouvez l'utiliser comme:

byte[] slice = foo.Slice(0, 40);
12
répondu Vladimir Mitrovic 2012-08-31 16:21:01

si vous voulez IEnumerable<byte> , alors juste

IEnumerable<byte> data = foo.Take(x);
11
répondu Marc Gravell 2009-01-02 10:53:42

vous pouvez utiliser un wrapper autour du tableau original (qui est IList), comme dans ce morceau de code (non testé).

public class SubList<T> : IList<T>
{
    #region Fields

private readonly int startIndex;
private readonly int endIndex;
private readonly int count;
private readonly IList<T> source;

#endregion

public SubList(IList<T> source, int startIndex, int count)
{
    this.source = source;
    this.startIndex = startIndex;
    this.count = count;
    this.endIndex = this.startIndex + this.count - 1;
}

#region IList<T> Members

public int IndexOf(T item)
{
    if (item != null)
    {
        for (int i = this.startIndex; i <= this.endIndex; i++)
        {
            if (item.Equals(this.source[i]))
                return i;
        }
    }
    else
    {
        for (int i = this.startIndex; i <= this.endIndex; i++)
        {
            if (this.source[i] == null)
                return i;
        }
    }
    return -1;
}

public void Insert(int index, T item)
{
    throw new NotSupportedException();
}

public void RemoveAt(int index)
{
    throw new NotSupportedException();
}

public T this[int index]
{
    get
    {
        if (index >= 0 && index < this.count)
            return this.source[index + this.startIndex];
        else
            throw new IndexOutOfRangeException("index");
    }
    set
    {
        if (index >= 0 && index < this.count)
            this.source[index + this.startIndex] = value;
        else
            throw new IndexOutOfRangeException("index");
    }
}

#endregion

#region ICollection<T> Members

public void Add(T item)
{
    throw new NotSupportedException();
}

public void Clear()
{
    throw new NotSupportedException();
}

public bool Contains(T item)
{
    return this.IndexOf(item) >= 0;
}

public void CopyTo(T[] array, int arrayIndex)
{
    for (int i=0; i<this.count; i++)
    {
        array[arrayIndex + i] = this.source[i + this.startIndex];
    }
}

public int Count
{
    get { return this.count; }
}

public bool IsReadOnly
{
    get { return true; }
}

public bool Remove(T item)
{
    throw new NotSupportedException();
}

#endregion

#region IEnumerable<T> Members

public IEnumerator<T> GetEnumerator()
{
    for (int i = this.startIndex; i < this.endIndex; i++)
    {
        yield return this.source[i];
    }
}

#endregion

#region IEnumerable Members

IEnumerator IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

#endregion

}

7
répondu Rauhotz 2009-01-02 11:42:10
byte[] foo = new byte[4096]; 

byte[] bar = foo.Take(40).ToArray();
6
répondu greyline 2014-01-20 23:33:18

si vous ne voulez pas ajouter LINQ ou d'autres extensions faites simplement:

float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();
6
répondu Dimitris 2016-07-01 13:50:36

vous pouvez utiliser la méthode Take extension

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);
4
répondu aku 2009-01-02 10:54:42

il peut s'agir d'une solution qui:

var result = foo.Slice(40, int.MaxValue);

, Puis le résultat est un IEnumerable< IEnumerable< byte>> avec un premier IEnumerable< byte> contient les 40 premiers octets de foo , et un second IEnumerable< byte> détient le reste.

j'ai écrit une classe d'Emballage, toute l'itération est paresseuse, j'espère qu'elle pourrait aider:

public static class CollectionSlicer
{
    public static IEnumerable<IEnumerable<T>> Slice<T>(this IEnumerable<T> source, params int[] steps)
    {
        if (!steps.Any(step => step != 0))
        {
            throw new InvalidOperationException("Can't slice a collection with step length 0.");
        }
        return new Slicer<T>(source.GetEnumerator(), steps).Slice();
    }
}

public sealed class Slicer<T>
{
    public Slicer(IEnumerator<T> iterator, int[] steps)
    {
        _iterator = iterator;
        _steps = steps;
        _index = 0;
        _currentStep = 0;
        _isHasNext = true;
    }

    public int Index
    {
        get { return _index; }
    }

    public IEnumerable<IEnumerable<T>> Slice()
    {
        var length = _steps.Length;
        var index = 1;
        var step = 0;

        for (var i = 0; _isHasNext; ++i)
        {
            if (i < length)
            {
                step = _steps[i];
                _currentStep = step - 1;
            }

            while (_index < index && _isHasNext)
            {
                _isHasNext = MoveNext();
            }

            if (_isHasNext)
            {
                yield return SliceInternal();
                index += step;
            }
        }
    }

    private IEnumerable<T> SliceInternal()
    {
        if (_currentStep == -1) yield break;
        yield return _iterator.Current;

        for (var count = 0; count < _currentStep && _isHasNext; ++count)
        {
            _isHasNext = MoveNext();

            if (_isHasNext)
            {
                yield return _iterator.Current;
            }
        }
    }

    private bool MoveNext()
    {
        ++_index;
        return _iterator.MoveNext();
    }

    private readonly IEnumerator<T> _iterator;
    private readonly int[] _steps;
    private volatile bool _isHasNext;
    private volatile int _currentStep;
    private volatile int _index;
}
3
répondu Li Zhen 2013-09-05 23:16:12

pour les tableaux d'octets système.Tampon.BlockCopy vous donnera la meilleure performance.

3
répondu Simon Giles 2016-02-03 20:29:42

dans C# 7.2 , vous pouvez utiliser Span<T> . L'avantage du nouveau système System.Memory est qu'il n'a pas besoin de copier autour des données.

la méthode dont vous avez besoin est Slice :

Span<byte> slice = foo.Slice(0, 40);

beaucoup de méthodes supportent maintenant Span et IReadOnlySpan , il sera donc très simple d'utiliser ce nouveau type.

noter qu'à l'époque de l'écriture du type Span<T> n'est pas encore défini dans la version la plus récente de .NET (4.7.1) donc pour l'utiliser vous devez installer le système .Paquet mémoire de NuGet.

3
répondu Patrick Hofman 2018-04-18 10:51:38

Voici une fonction d'extension qui utilise un générique et se comporte comme la fonction PHP array_slice . Un décalage et une longueur négatifs sont autorisés.

public static class Extensions
{
    public static T[] Slice<T>(this T[] arr, int offset, int length)
    {
        int start, end;

        // Determine start index, handling negative offset.
        if (offset < 0)
            start = arr.Length + offset;
        else
            start = offset;

        // Clamp start index to the bounds of the input array.
        if (start < 0)
            start = 0;
        else if (start > arr.Length)
            start = arr.Length;

        // Determine end index, handling negative length.
        if (length < 0)
            end = arr.Length + length;
        else
            end = start + length;

        // Clamp end index to the bounds of the input array.
        if (end < 0)
            end = 0;
        if (end > arr.Length)
            end = arr.Length;

        // Get the array slice.
        int len = end - start;
        T[] result = new T[len];
        for (int i = 0; i < len; i++)
        {
            result[i] = arr[start + i];
        }
        return result;
    }
}
1
répondu Brendan Taylor 2014-10-25 12:30:27

Je ne pense pas que C# supporte la sémantique de gamme. Vous pouvez cependant écrire une méthode d'extension, comme:

public static IEnumerator<Byte> Range(this byte[] array, int start, int end);

mais comme d'autres ont dit si vous n'avez pas besoin de définir un index de départ alors Take est tout ce dont vous avez besoin.

1
répondu bleevo 2016-07-01 13:45:23