সি # তে অ্যারের স্লাইস


228

তুমি এটা কিভাবে কর? একটি বাইট অ্যারে দেওয়া:

byte[] foo = new byte[4096];

আমি আলাদা অ্যারে হিসাবে অ্যারের প্রথম এক্স বাইট কীভাবে পেতে পারি? (বিশেষত, আমার এটি হিসাবে প্রয়োজন IEnumerable<byte>)

এটি Socketএস এর সাথে কাজ করার জন্য । আমি মনে করি সবচেয়ে সহজ উপায় পার্ল সিনট্যাক্সের মতো অ্যারে স্লাইসিং হবে:

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

যা @barঅ্যারে প্রথম 41 টি উপাদান ফেরত দেবে । সি # তে এমন কিছু আছে যা আমি স্রেফ অনুপস্থিত, বা আমার অন্য কিছু করা উচিত?

লিনকুই আমার জন্য (.NET 3.5) বিকল্প, যদি এটি কোনওরকম সহায়তা করে।


3
এরে slicing C # প্রস্তাব হল 7.2 github.com/dotnet/csharplang/issues/185
মার্ক

3
সি # 8.0 দেশীয় অ্যারে স্লাইসিংয়ের পরিচিতি দেখবে। আরও তথ্যের জন্য উত্তর দেখুন
রেমি

1
আপনার আগ্রহী হতে পারে অ্যারেস্লাইস <টি> যা মূল তথ্যগুলির উপরে দৃষ্টিভঙ্গি হিসাবে পদক্ষেপের সাথে অ্যারেগুলি স্লাইসিং প্রয়োগ করে: github.com/henon/SliceAndDice
হেনন

উত্তর:


196

অ্যারেগুলি অগণনীয়, সুতরাং আপনার fooইতিমধ্যে এটি IEnumerable<byte>নিজেই। Take()আপনি যা চান তা পেতে কেবল লিনকিউ সিকোয়েন্স পদ্ধতিগুলি ব্যবহার করুন (এর Linqসাথে নামস্থানটি অন্তর্ভুক্ত করতে ভুলবেন না using System.Linq;):

byte[] foo = new byte[4096];

var bar = foo.Take(41);

আপনার যদি কোনও IEnumerable<byte>মান থেকে সত্যই প্রয়োজন হয় তবে আপনি তার জন্য ToArray()পদ্ধতিটি ব্যবহার করতে পারেন । এখানে বিষয়টি মনে হয় না।


5
যদি আমরা অন্য অ্যারেতে অনুলিপি করতে চলেছি তবে কেবল অ্যারে ব্যবহার করুন op কপি স্ট্যাটিক পদ্ধতি। তবে আমি মনে করি যে অন্যান্য উত্তরগুলির উদ্দেশ্যটি সঠিকভাবে ব্যাখ্যা করেছে, অন্য 41 টি অ্যারে কেবল একটি আইনামেবল <বিট> প্রয়োজন নেই যা প্রথম 41 বাইটের বেশি।
অ্যান্থনিডাব্লু জোন্স

2
মনে রাখবেন যে কেবলমাত্র একক মাত্রিক এবং দাগযুক্ত অ্যারেগুলি প্রচুর পরিমাণে, বহু মাত্রিক অ্যারেগুলি নয়।
আবেল

11
অ্যারে ব্যবহার করে নোট.কপি লিনকিউ'র টেক বা স্কিপ পদ্ধতি ব্যবহারের চেয়ে অনেক দ্রুত সম্পাদন করে।
মাইকেল

4
@ আবেল এটি আসলে খুব ভুল। মাল্টি মাত্রিক অ্যারে হয় গণনীয় কিন্তু তারা ভালো গনা: [2,3] => [1,1], [1,2], [1,3], [2,1], [2,2], [2,3]। জেগড অ্যারেগুলিও অগণনীয় তবে গণনার সময় কোনও মান ফিরিয়ে দেওয়ার পরিবর্তে তারা তাদের অভ্যন্তরীণ অ্যারেটি ফিরিয়ে দেয়। type[][] jaggedArray; foreach (type[] innerArray in jaggedArray) { }
এটির

3
@ এডিয়াকাপি "খুব বেড়ান"? ;)। তবে আপনি আংশিকভাবে সঠিক, আমার "মাল্টিমিডিম অ্যারেগুলি প্রয়োগ করে না" রাইট করা উচিত ছিল IEnumerable<T>, তবে আমার বক্তব্যটি আরও পরিষ্কার হবে would এটিও দেখুন: stackoverflow.com/questions/721882/…
আবেল

211

আপনি ব্যবহার করতে পারে ArraySegment<T>। এটি খুব হালকা ওজন হওয়ায় এটি অ্যারে অনুলিপি করে না:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );

5
দুর্ভাগ্যক্রমে এটি অনুমেয় নয়।
পুনরাবৃত্তি

1
সত্য, তবে এটির চারপাশে একটি পুনরাবৃত্তাকার মোড়ক লেখা সহজ হবে যা আইয়নামেবল কার্যকর করে।
মাইক স্কট

22
কেউ কি জানে কেন এটি অনুমিত নয়? আমি না। দেখে মনে হচ্ছে এটি হওয়া উচিত।
ফ্যানটিয়াস

39
অ্যারেসিগমেন্ট IList এবং .Net 4.5 থেকে শুরু করে গণ্যযোগ্য। পুরানো সংস্করণ ব্যবহারকারীদের জন্য খুব খারাপ ..
টড লি

6
@ জ্যো আমার অর্থ অ্যারেসেজমেন্ট <T> আইনিউবারেবল <টি> প্রয়োগ করে N.৪ নেট থেকে শুরু করুন, আইএননামেবল নয় <টি> নিজেই নতুন।
টড লি

137

আপনি অ্যারে CopyTo()পদ্ধতিটি ব্যবহার করতে পারেন ।

বা লিনকিউ দিয়ে আপনি ব্যবহার করতে পারেন Skip()এবং Take()...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);

1
একটি ভাল ধারণার জন্য +1, তবে আমার অন্য ফাংশনের ইনপুট হিসাবে ফেরত অ্যারে ব্যবহার করা দরকার, যা কপিটোকে একটি অস্থায়ী পরিবর্তনশীল প্রয়োজন। আমি এখনও অন্য উত্তরের জন্য অপেক্ষা করব।
ম্যাথু শার্লে

4
আমি লিনকিউ এর সাথে এখনও পরিচিত নই, সম্ভবত এটিই আমার আরও প্রমাণ হতে পারে যে আমার সত্যই হওয়া উচিত।
ম্যাথু শার্লে

11
এই পদ্ধতিটি অ্যারে.কপির চেয়ে কমপক্ষে 50x ধীর। এটি অনেক পরিস্থিতিতে ইস্যু নয় তবে যখন চক্রের অ্যারে স্লাইসিং করা হয় তখন পারফরম্যান্সের ড্রপ খুব স্পষ্ট।
ভ্যালেন্টিন ভ্যাসিলিভ

3
আমি একক কল করছি, সুতরাং অভিনয় আমার জন্য সমস্যা নয় for পাঠযোগ্যতার জন্য এটি দুর্দান্ত ... ধন্যবাদ
ধনী

2
জন্য ধন্যবাদ Skip()। শুধু Take()আপনি একটি স্বেচ্ছাসেবী স্লাইস পাবেন না। তদ্ব্যতীত, আমি যাইহোক লিনকিউ সমাধানের সন্ধান করছিলাম (টুকরো টুকরো টুকরো, তবে আমি জানতাম অ্যারে সম্পর্কে ফলাফলগুলি খুঁজে পাওয়া সহজ হবে)।
টমসজ গ্যান্ডার

55
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);

11
আমি মনে করি বাফার.ব্লককপি () আরও দক্ষ এবং একই ফলাফল অর্জন করে।
ম্যাট ডেভিস

28

সি # 8.0 /। নেট কোর 3.0 থেকে শুরু হচ্ছে

অ্যারে স্লাইসিং নতুন ধরণের Indexএবং যুক্ত Rangeহওয়ার সাথে সাথে সমর্থনযোগ্য হবে ।

রেঞ্জ স্ট্রাক্ট ডক্স
সূচিপত্র কাঠামো ডক্স

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

var slice = a[i1..i2]; // { 3, 4, 5 }

সি # 8.0 ব্লগ থেকে নেওয়া উপরে কোডের নমুনা ।

নোট করুন যে ^উপসর্গটি অ্যারের শেষ থেকে গণনা নির্দেশ করে। যেমন দস্তাবেজের উদাহরণে দেখানো হয়েছে

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

Rangeএবং Indexঅ্যারে স্লাইসিংয়ের বাইরেও কাজ করেন, উদাহরণস্বরূপ লুপগুলি সহ

Range range = 1..4; 
foreach (var name in names[range])

1 থেকে 4 এন্ট্রি লুপ করবে


নোট করুন যে এই উত্তরটি লেখার সময়, সি # 8.0 এখনও আনুষ্ঠানিকভাবে সি # 8.x প্রকাশিত হয়নি
এবং নেট নেট 3.x এখন ভিজ্যুয়াল স্টুডিও 2019 এবং এর পরে উপলব্ধ


এই অ্যারে একটি অনুলিপি তৈরি বা না কোন ধারণা?
টিম পোহলমান

2
দেখে মনে হচ্ছে এটি একটি অনুলিপি: কোড journey.net/2019/02/csharp-8-slicing-indexes-ranges
টিম

22

ইন সি # 7.2 , আপনি ব্যবহার করতে পারেন Span<T>। নতুন System.Memoryসিস্টেমটির সুবিধা হ'ল এটির উপাত্তের অনুলিপি করার দরকার নেই।

আপনার যে পদ্ধতিটি প্রয়োজন তা হ'ল Slice:

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

প্রচুর পদ্ধতি এখন সমর্থন করে Spanএবং IReadOnlySpanতাই এই নতুন প্রকারটি ব্যবহার করা খুব সোজা হবে।

নোট করুন যে Span<T>টাইপটি লেখার সময় .NET এর অতি সাম্প্রতিক সংস্করণে এখনও সংজ্ঞায়িত করা হয়নি (4.7.1) সুতরাং এটি ব্যবহার করার জন্য আপনাকে নুগেট থেকে সিস্টেম.মেমরি প্যাকেজ ইনস্টল করতে হবে ।


1
মনে রাখবেন যে Span<T>প্রকারটি .NET এর অতি সাম্প্রতিক সংস্করণে এখনও সংজ্ঞায়িত করা হয়নি (4.7.1) তাই এটি ব্যবহার করতে আপনাকে নুগেট থেকে ইনস্টল করতে হবে System.Memory(এবং নুগেটে এটি অনুসন্ধান করার সময় "অন্তর্ভুক্ত করা পূর্বনির্ধারিত" টিক চিহ্নটি মনে রাখতে হবে)
ম্যাথু ওয়াটসন

ধন্যবাদ ম্যাথিউ ওয়াটসন আমি আপনার মন্তব্যটি আবার লিখেছিলাম এবং আমার উত্তরে এটি যুক্ত করেছি।
প্যাট্রিক হফম্যান

16

আর একটি সম্ভাবনা যা আমি এখানে উল্লেখ করি নি: বাফার.ব্লককপি () অ্যারে.কপি () এর চেয়ে কিছুটা দ্রুত এবং এটি আদিমতার অ্যারে থেকে অন-ফ্লাইতে রূপান্তর করতে সক্ষম হওয়ার অতিরিক্ত সুবিধা রয়েছে (বলুন, সংক্ষিপ্ত []] বাইটের একটি অ্যারে, যা আপনি সকেটের উপর দিয়ে সঞ্চার করতে হবে এমন সংখ্যাসূচক অ্যারে পেয়ে গেলে সহজেই হতে পারে।


2
Buffer.BlockCopyArray.Copy()তারা একই পরামিতিগুলি স্বীকার করেও বিভিন্ন ফলাফল উত্পন্ন করেছে - প্রচুর শূন্য উপাদান রয়েছে। কেন?
জোকুল

7
@ জোকুল - তারা আসলে একই পরামিতিগুলি গ্রহণ করে না। অ্যারে.কপি () উপাদানগুলিতে এর দৈর্ঘ্য এবং অবস্থানের পরামিতি নেয়। বাফার.ব্লককপি () এর দৈর্ঘ্য এবং অবস্থানের প্যারামিটারগুলি বাইটে নেয়। অন্য কথায়, আপনি যদি পূর্ণসংখ্যার 10-উপাদান অ্যারে অনুলিপি করতে চান তবে আপনি ব্যবহার করতে পারেন Array.Copy(array1, 0, array2, 0, 10)তবে Buffer.BlockCopy(array1, 0, array2, 0, 10 * sizeof(int))
কেন স্মিথ


14

এখানে একটি সাধারণ এক্সটেনশন পদ্ধতি যা নতুন অ্যারে হিসাবে একটি স্লাইস প্রদান করে:

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;
}

তারপরে আপনি এটি ব্যবহার করতে পারেন:

byte[] slice = foo.Slice(0, 40);

8

আপনি যদি লিনকিউ বা অন্যান্য এক্সটেনশান যুক্ত করতে না চান তবে কেবল করুন:

float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();

Error CS0246: The type or namespace name 'List<>' could not be found (are you missing a using directive or an assembly reference?) মাইক্রোসফ্ট ডকুমেন্টেশন শত শত "তালিকা" এন্ট্রি সূচী সহ আশাবাদী। এখানে সঠিক কি?
wallyk

1
System.Collections.Generic.List
টেট্রালাক্স

7

আপনি মূল অ্যারে (যা IList) এর চারপাশে একটি মোড়ক ব্যবহার করতে পারেন, কোডটির এই (অনির্ধারিত) অংশের মতো।

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

}


4
আমি ইন্ডেক্সঅফের জন্য ইক্যুয়ালিটি কম্পিউটারের ডিফল্টটি ব্যবহার করার পরামর্শ দেব - এইভাবে আপনার কোনও বিশেষ কেসিং লাগবে না।
জন স্কিটি

1
আমি আশা করি এটি একদম ঠিক আছে। আমি অবশ্যই প্রথমে সহজ কোড সহ যাব।
জন স্কিটি

এরকম কিছু হল আমার মতে সেরা উপায় best তবে স্পষ্টতই এটি সরল চেয়ে বেশি কাজ (প্রথমবারের মতো) Array.Copy, যদিও এর অনেক সুবিধা থাকতে পারে, যেমন সাবস্টিস্টটি আক্ষরিকভাবে পিতামাতার তালিকার একটি অঞ্চল হিসাবে তালিকার এন্ট্রিগুলির অনুলিপি না করে।
iদিয়াকপি


6

বাইট অ্যারে সিস্টেমের জন্য.বফার.ব্লককপি আপনাকে খুব ভাল পারফরম্যান্স দেবে।


1
আপনি যদি হাজার হাজার বা মিলিয়ন বার লুপে এটি করে থাকেন তবে এটি কেবলমাত্র গুরুত্বপূর্ণ। সকেট অ্যাপ্লিকেশনটিতে, আপনি সম্ভবত কিছু ইনপুট নিচ্ছেন এবং এটি কিছু অংশে ভেঙেছেন। আপনি যদি কেবল একবার এটি করেন তবে পরের প্রোগ্রামারটি যে খুব সহজেই বুঝতে পারে তা সবচেয়ে ভাল পারফরম্যান্স।
মাইকেল ব্ল্যাকবার্ন

5

আপনি এক্সটেনশন পদ্ধতিটি ব্যবহার করতে পারেন

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);

3

এটি একটি সমাধান হতে পারে যা:

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

তারপরে ফলাফলটি একটি IEnumerable <IEnumerable <বাইট >> সাথে প্রথম IEnumerable <বাইট> এতে foo এর প্রথম 40 বাইট থাকে এবং একটি দ্বিতীয় IEnumerable <বাইট> বাকীটি ধারণ করে।

আমি একটি মোড়কের ক্লাস লিখেছি, পুরো পুনরাবৃত্তিটি অলস, আশা করি এটি সহায়তা করতে পারে:

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;
}

2

আমি মনে করি না যে সি # রেঞ্জ শব্দার্থকে সমর্থন করে। আপনি যদিও কোনও এক্সটেনশন পদ্ধতি লিখতে পারেন, যেমন:

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

তবে অন্যরা যেমন বলেছে যে আপনার যদি সূচনা সূচি সেট করার দরকার না হয় তবে Takeআপনার যা প্রয়োজন তা হল।


1

এখানে একটি এক্সটেনশন ফাংশন যা জেনেরিক ব্যবহার করে এবং পিএইচপি ফাংশন অ্যারে_স্লাইসের মতো আচরণ করে । নেতিবাচক অফসেট এবং দৈর্ঘ্য অনুমোদিত।

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
বেশ ভাল, যদিও নেট নেট থেকে কয়েকটি জিনিস। যদি start0 এবং এর মধ্যে না হয় তবে arr.Lengthএটি সম্ভবত সীমার ব্যতিক্রম ছুঁড়ে ফেলা উচিত। এছাড়াও, end >= start >= 0সুতরাং আপনার যাচাই করার দরকার নেই end < 0, এটি হওয়ার সম্ভাবনা নেই। আপনি সম্ভবত এটি আরও চূড়ান্তভাবে পরীক্ষা করে length >= 0এবং তারপরে len = Math.min(length, arr.Length - start)ফডিংয়ের পরিবর্তে আরও বেশি করে করতে পারেন end
ম্যাথু শার্লে

0
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace data_seniens
{
    class Program
    {
        static void Main(string[] args)
        {
            //new list
            float [] x=new float[]{11.25f,18.0f,20.0f,10.75f,9.50f, 11.25f, 18.0f, 20.0f, 10.75f, 9.50f };

            //variable
            float eat_sleep_area=x[1]+x[3];
            //print
            foreach (var VARIABLE in x)
            {
                if (VARIABLE < x[7])
                {
                    Console.WriteLine(VARIABLE);
                }
            }



            //keep app run
        Console.ReadLine();
        }
    }
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.