Bir dizeyi belirli bir boyuttaki parçalara bölme


218

Bir dizem olduğunu varsayalım:

string str = "1111222233334444"; 

Bu dizeyi bir boyutta nasıl parçalara ayırabilirim?

örneğin, bunu 4 boyutlarına bölmek dizeleri döndürür:

"1111"
"2222"
"3333"
"4444"

18
Neden C # 'ın standart dize düzenleme fonksiyonları bunu daha az çaba ve daha hızlı yapabilirken LINQ veya regexes kullanalım? Ayrıca, dize tek sayıda karakter uzunluğundaysa ne olur?
Ian Kemp

7
"Döngülerden kaçınmak istiyorum" - neden?
Mitch Wheat

12
Basit bir döngü kullanmak kesinlikle en iyi performansı verir.
Guffa

4
nichesoftware.co.nz/blog/200909/linq-vs-loop-performance , linq ve bir dizi üzerinden gerçek döngü arasında oldukça iyi bir karşılaştırmadır. Şimdiye kadar el ile yazılmış kod daha hızlı bulacağınızdan şüpheliyim çünkü optimize etmek zor olan çalışma zamanı delegelerini çağırıyor. Linq daha eğlenceli olsa :)
Blindy

2
LINQ veya regexes kullanıyor olun, döngü hala oradadır.
Anton Tykhyy

Yanıtlar:


247
static IEnumerable<string> Split(string str, int chunkSize)
{
    return Enumerable.Range(0, str.Length / chunkSize)
        .Select(i => str.Substring(i * chunkSize, chunkSize));
}

Kenar durumlarını ( nullveya boş giriş dizesi chunkSize == 0, giriş dizesi uzunluğu bölünemez chunkSize, vb.) İşlemek için ek kod gerekebileceğini lütfen unutmayın . Orijinal soru, bu uç durumlar için herhangi bir gereklilik belirtmez ve gerçek hayatta gereksinimler değişebilir, bu nedenle bu cevabın kapsamı dışındadır.


3
@Harry İyi yakala! Bu, alt dizenin count parametresindeki bir damla üçlü ifadeyle giderilebilir. Gibi bir şey: (i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize. Ek bir sorun, bu işlevin str boş olduğunu hesaba katmıyor olmasıdır. Bu da başka bir üçlü ifadesinde bütün return deyimini sarma düzeltilebilir: (str != null) ? ... : Enumerable.Empty<String>();.
Drew Spickes

7
Bu yakın, ama daha önceki 30 upvoters aksine ben den Range döngü sayısı sınırını değiştirmek zorunda str.Length / chunkSizeiçindouble length = str.Length; double size = chunkSize; int count = (int)Math.Ceiling(length/size); return Enumerable.Range(0, count)...
boşluğu

4
@KonstantinSpirin Kodun çalışıp çalışmadığını kabul ediyorum. Yalnızca bir dizenin chunkSize öğesinin katı olduğu durumu işler, dizenin geri kalanı kaybolur. Lütfen düzeltin. Ayrıca, LINQ ve sihrinin sadece bu sorunun çözümüne bakmak isteyen biri için anlaşılması kolay olmadığını unutmayın. Kişi artık Enumerable.Range () ve .Select () işlevlerinin ne yaptığını anlamalıdır. Bu işlevler yıllardır BCL'de olduğu için C # /. NET kodu yazmak için bir anlayışa sahip olmanız gerektiğini iddia etmeyeceğim.
CodeMonkeyKing

6
Konu başlatıcı yorumlarda söyledi StringLength % 4 will always be 0. Anlaması Linqkolay değilse , döngüler ve verimler kullanan başka cevaplar da vardır. Herkes en çok sevdiği çözümü seçmekte özgürdür. Kodunuzu yanıt olarak gönderebilirsiniz ve insanlar buna oy vereceklerdir.
Konstantin Spirin

3
Enumerable.Range (0, (str.Length + chunkSize - 1) / chunkSize) .Seç (i => str.Substring (i * chunkSize, Math.Min (str.Length - chunkSize, chunkSize))
Sten Petrov

135

Güvercin + Konstatin'in cevaplarının bir kombinasyonu ...

static IEnumerable<string> WholeChunks(string str, int chunkSize) {
    for (int i = 0; i < str.Length; i += chunkSize) 
        yield return str.Substring(i, chunkSize);
}

Bu, çok sayıda parçaya bölünebilen tüm dizeler için çalışır ve aksi takdirde bir istisna atar.

Herhangi bir uzunlukta dizeleri desteklemek istiyorsanız, aşağıdaki kodu kullanabilirsiniz:

static IEnumerable<string> ChunksUpto(string str, int maxChunkSize) {
    for (int i = 0; i < str.Length; i += maxChunkSize) 
        yield return str.Substring(i, Math.Min(maxChunkSize, str.Length-i));
}

Ancak, OP açıkça buna ihtiyaç duymadığını; biraz daha uzun ve okunması zor, biraz daha yavaş. KISS ve YAGNI ruhuna göre, ilk seçenekle giderdim: muhtemelen mümkün olan en verimli uygulama ve çok kısa, okunabilir ve daha da önemlisi, uygunsuz girdi için bir istisna atıyor.


4
Bir başıyla +1 değerinde. biraz kafasına çivi vurur. kısa ve öz bir sistem arıyor ve siz de (muhtemelen) daha iyi bir performans sergiliyorsunuz.
güvercin

7
Ve "statik ... Chunk (bu dize str, int chunkSize) {" yaparsanız, içine bir tane daha "yeni" C # -Özellik daha gelir. Sonra "1111222233334444" yazabilirsiniz. Hurda (4).
MartinStettner

1
@MartinStettner: Bu ortak bir işlemse kesinlikle iyi bir fikir.
Eamon Nerbonne

Yalnızca ikinci kodu eklemelisiniz. Birincisi, kullanmadan önce dizenin yığın boyutunun katı olduğunu anlamanız ve test etmenizi veya dizenin geri kalanını döndürmeyeceğini anlamanız gerekir.
CodeMonkeyKing

OP'nin sorusu bu işlevselliğe ihtiyaç duyup duymadığını netleştirmiyor. Dize belirtilen yığın boyutuna eşit olarak bölünemezse, ilk çözüm daha basit, daha hızlı ve güvenilir bir şekilde başarısız olur. "Yanlış" sonuçlar döndürmenin kötü olacağını kabul ediyorum, ama yaptığı şey değil - sadece bir istisna atıyor, bu yüzden sınırlama ile yaşayabilirseniz kullanmakta sorun olmaz.
Eamon Nerbonne

56

Neden döngüler olmasın? İşte oldukça iyi yapacak bir şey:

        string str = "111122223333444455";
        int chunkSize = 4;
        int stringLength = str.Length;
        for (int i = 0; i < stringLength ; i += chunkSize)
        {
            if (i + chunkSize > stringLength) chunkSize = stringLength  - i;
            Console.WriteLine(str.Substring(i, chunkSize));

        }
        Console.ReadLine();

Ben dize 4 faktörü değil durumda, ancak bir döngü basit bir çok iyi yaparsa bunun için motivasyon merak değil, fikrinin mümkün olmadığını söylemek değil, nasıl başa olacağını bilmiyorum? Açıkçası, yukarıdakiler temizlenebilir ve hatta bir uzatma yöntemi olarak yerleştirilebilir.

Veya yorumlarda belirtildiği gibi, bunun / 4 olduğunu biliyorsunuz

str = "1111222233334444";
for (int i = 0; i < stringLength; i += chunkSize) 
  {Console.WriteLine(str.Substring(i, chunkSize));} 

1
int chunkSize = 4Döngünün dışını çekebilirsiniz . Yalnızca son geçişte değiştirilecektir.
John Feminella

Basit ve etkili bir çözüm için +1 - i += chunkSizebunun yerine bunu yapardım, ancak bunu yapardım .
Ian Kemp

Muhtemelen küçük bir kelime oyunu, ama muhtemelen str.Lengthdöngüden ve yerel bir değişkene de çekmelisiniz. C # optimizer satır uzunluğu inline mümkün olabilir , ancak yazılı kod strasla etkili olduğu için verimli değil, her döngüde bir yöntem çağrısı yapacağını düşünüyorum .
Daniel Pryden

@ Daniel, fikrini oraya koy. Ancak bunun çalışma zamanında hesaplanmayacağından emin değilim, ama bu başka bir soru;)
güvercin

@ Daniel buna geri dönüyor, bu optimizasyonun derleyici tarafından çıkarılacağından eminiz.
güvercin

41

Düzenli ifadeler ve Linq kullanma :

List<string> groups = (from Match m in Regex.Matches(str, @"\d{4}")
                       select m.Value).ToList();

Bunu daha okunabilir buluyorum, ama bu sadece kişisel bir görüş. Ayrıca bir astar olabilir :).


7
Deseni @ "\ d {1,4}" olarak değiştirin ve herhangi bir dize uzunluğu için çalışır. :)
Guffa

3
+1 Bu diğer çözümlerden daha yavaş olmasına rağmen kesinlikle okunabilir. OP'nin rakamlar mı yoksa rastgele karakterler mi gerektireceği net değil; büyük olasılıkla \dkarakter sınıfını bir .ve ile belirtmek akıllıca olur RegexOptions.Singleline.
Eamon Nerbonne

2
veya sadece Regex.Matches (s, @ "\ d {1,4}"). (m => m.Value) .ToList (); Sadece uzatma yöntemlerini kullandığımızı gizlemeye yarayan bu alternatif sözdiziminin amacını hiç bulamadım.
Dag

38

Bu @ güvercin çözümüne dayanmaktadır ancak bir genişletme yöntemi olarak uygulanmaktadır.

Yararları:

  • Genişletme yöntemi
  • Köşe kılıflarını kapsar
  • Dizeyi herhangi bir karakterle böler: sayılar, harfler, diğer simgeler

kod

public static class EnumerableEx
{    
    public static IEnumerable<string> SplitBy(this string str, int chunkLength)
    {
        if (String.IsNullOrEmpty(str)) throw new ArgumentException();
        if (chunkLength < 1) throw new ArgumentException();

        for (int i = 0; i < str.Length; i += chunkLength)
        {
            if (chunkLength + i > str.Length)
                chunkLength = str.Length - i;

            yield return str.Substring(i, chunkLength);
        }
    }
}

kullanım

var result = "bobjoecat".SplitBy(3); // bob, joe, cat

Kısalık için birim testleri kaldırıldı ( önceki revizyona bakın )


İlginç bir çözüm, ancak girişte boş if (str.Length == 0) yield return String.Empty; else { for... }
değerin

Yani, normal String.Split boş dizeleri bu şekilde işler; boş bir dize girdisi döndürür.
Nyerguds

Yan not: kullanım örneğiniz yanlış. Sadece IEnumerablediziye yayın yapamazsınız , özellikle dolaylı olarak değil.
Nyerguds

Şahsen ben bu yöntemi çağırmaktan hoşlanıyorum Chunkify.. Bu benim değil, bu ismi nerede gördüğümü hatırlamıyorum, ama bana çok hoş geldi
quetzalcoatl

20

Tek astar için bu nasıl?

List<string> result = new List<string>(Regex.Split(target, @"(?<=\G.{4})", RegexOptions.Singleline));

Bu normal ifadeyle, son öbek dört karakterden daha az olup olmadığı önemli değildir, çünkü sadece arkasındaki karakterlere bakar.

Eminim bu en etkili çözüm değildir, ama sadece oraya atmak zorunda kaldım.


bu durumda target.Lenght % ChunckSize == 0ek bir boş satır döndürürse örneğinList<string> result = new List<string>(Regex.Split("fooo", @"(?<=\G.{4})", RegexOptions.Singleline));
fubo

9

Güzel değil ve hızlı değil, ama işe yarıyor, tek katmanlı ve LINQy:

List<string> a = text.Select((c, i) => new { Char = c, Index = i }).GroupBy(o => o.Index / 4).Select(g => new String(g.Select(o => o.Char).ToArray())).ToList();

GroupBy öğelerinin sırasını koruduğu garanti ediliyor mu?
Konstantin Spirin

ToCharArrayçünkü gereksizdir stringDİR IEnumerable<char>.
juharr

8

Son zamanlarda işte bunu başaran bir şey yazmak zorunda kaldım, bu yüzden çözümümü bu soruna göndereceğimi düşündüm. Ek bir avantaj olarak, bu çözümün işlevselliği, dizeyi ters yönde bölmenin bir yolunu sağlar ve yukarıda Marvin Pinto tarafından daha önce belirtildiği gibi unicode karakterleri doğru şekilde işler. İşte burada:

using System;
using Extensions;

namespace TestCSharp
{
    class Program
    {
        static void Main(string[] args)
        {    
            string asciiStr = "This is a string.";
            string unicodeStr = "これは文字列です。";

            string[] array1 = asciiStr.Split(4);
            string[] array2 = asciiStr.Split(-4);

            string[] array3 = asciiStr.Split(7);
            string[] array4 = asciiStr.Split(-7);

            string[] array5 = unicodeStr.Split(5);
            string[] array6 = unicodeStr.Split(-5);
        }
    }
}

namespace Extensions
{
    public static class StringExtensions
    {
        /// <summary>Returns a string array that contains the substrings in this string that are seperated a given fixed length.</summary>
        /// <param name="s">This string object.</param>
        /// <param name="length">Size of each substring.
        ///     <para>CASE: length &gt; 0 , RESULT: String is split from left to right.</para>
        ///     <para>CASE: length == 0 , RESULT: String is returned as the only entry in the array.</para>
        ///     <para>CASE: length &lt; 0 , RESULT: String is split from right to left.</para>
        /// </param>
        /// <returns>String array that has been split into substrings of equal length.</returns>
        /// <example>
        ///     <code>
        ///         string s = "1234567890";
        ///         string[] a = s.Split(4); // a == { "1234", "5678", "90" }
        ///     </code>
        /// </example>            
        public static string[] Split(this string s, int length)
        {
            System.Globalization.StringInfo str = new System.Globalization.StringInfo(s);

            int lengthAbs = Math.Abs(length);

            if (str == null || str.LengthInTextElements == 0 || lengthAbs == 0 || str.LengthInTextElements <= lengthAbs)
                return new string[] { str.ToString() };

            string[] array = new string[(str.LengthInTextElements % lengthAbs == 0 ? str.LengthInTextElements / lengthAbs: (str.LengthInTextElements / lengthAbs) + 1)];

            if (length > 0)
                for (int iStr = 0, iArray = 0; iStr < str.LengthInTextElements && iArray < array.Length; iStr += lengthAbs, iArray++)
                    array[iArray] = str.SubstringByTextElements(iStr, (str.LengthInTextElements - iStr < lengthAbs ? str.LengthInTextElements - iStr : lengthAbs));
            else // if (length < 0)
                for (int iStr = str.LengthInTextElements - 1, iArray = array.Length - 1; iStr >= 0 && iArray >= 0; iStr -= lengthAbs, iArray--)
                    array[iArray] = str.SubstringByTextElements((iStr - lengthAbs < 0 ? 0 : iStr - lengthAbs + 1), (iStr - lengthAbs < 0 ? iStr + 1 : lengthAbs));

            return array;
        }
    }
}

Ayrıca, bu kodun çalıştırılmasının sonuçlarına ilişkin bir resim bağlantısı: http://i.imgur.com/16Iih.png


1
Bu kodla ilgili bir sorun fark ettim. {str.ToString()}İlk IF ifadenizin sonunda var . Kastetmediğinden emin misin str.String? Yukarıdaki kodla ilgili bir sorun yaşadım, bu değişikliği yaptım ve her şey işe yaradı.
gunr2171

@ gunr2171 Görünüşe göre str == null olursa, bu satır bir NullReferenceException da verir.
John Zabroski

5

Bu, LINQ veya burada kullanılan diğer yaklaşımları kullanmaktan çok daha hızlı ve daha verimli olmalıdır.

public static IEnumerable<string> Splice(this string s, int spliceLength)
{
    if (s == null)
        throw new ArgumentNullException("s");
    if (spliceLength < 1)
        throw new ArgumentOutOfRangeException("spliceLength");

    if (s.Length == 0)
        yield break;
    var start = 0;
    for (var end = spliceLength; end < s.Length; end += spliceLength)
    {
        yield return s.Substring(start, spliceLength);
        start = end;
    }
    yield return s.Substring(start);
}

Bu erken kontrol yapıyor gibi görünüyor , ama değil. Numaralandırılmaya başlanana kadar hata almazsınız. İlk bölümün argüman kontrolünü yaptığı ve sonra numaralandırmayı yapan ikinci özel bölümün sonuçlarını döndürdüğü işlevinizi iki bölüme ayırmanız gerekir .
ErikE

4
public static IEnumerable<IEnumerable<T>> SplitEvery<T>(this IEnumerable<T> values, int n)
{
    var ls = values.Take(n);
    var rs = values.Skip(n);
    return ls.Any() ?
        Cons(ls, SplitEvery(rs, n)) : 
        Enumerable.Empty<IEnumerable<T>>();
}

public static IEnumerable<T> Cons<T>(T x, IEnumerable<T> xs)
{
    yield return x;
    foreach (var xi in xs)
        yield return xi;
}

4

Jon Skeet tarafından morelinq kullanabilirsiniz . Yığın gibi kullanın :

string str = "1111222233334444";
int chunkSize = 4;
var chunks = str.Batch(chunkSize).Select(r => new String(r.ToArray()));

Bu, dize için 4 parça döndürür "1111222233334444". Dize uzunluğu yığın boyutundan küçük veya ona eşitseBatch , dizeyi tek öğe olarak döndürürIEnumerable<string>

Çıktı için:

foreach (var chunk in chunks)
{
    Console.WriteLine(chunk);
}

ve verecek:

1111
2222
3333
4444

MoreLINQ yazarları arasında Jonathan Skeet'i görüyorum , ama Jon Skeet görmüyorum . Yani demek istedi Jon Skeet, ya da ne? ;-)
Sнаđошƒаӽ

3
static IEnumerable<string> Split(string str, double chunkSize)
{
    return Enumerable.Range(0, (int) Math.Ceiling(str.Length/chunkSize))
       .Select(i => new string(str
           .Skip(i * (int)chunkSize)
           .Take((int)chunkSize)
           .ToArray()));
}

ve başka bir yaklaşım:

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

public class Program
{
    public static void Main()
    {

        var x = "Hello World";
        foreach(var i in x.ChunkString(2)) Console.WriteLine(i);
    }
}

public static class Ext{
    public static IEnumerable<string> ChunkString(this string val, int chunkSize){
        return val.Select((x,i) => new {Index = i, Value = x})
                  .GroupBy(x => x.Index/chunkSize, x => x.Value)
                  .Select(x => string.Join("",x));
    }
}

3

Altı yıl sonra o_O

Sadece

    public static IEnumerable<string> Split(this string str, int chunkSize, bool remainingInFront)
    {
        var count = (int) Math.Ceiling(str.Length/(double) chunkSize);
        Func<int, int> start = index => remainingInFront ? str.Length - (count - index)*chunkSize : index*chunkSize;
        Func<int, int> end = index => Math.Min(str.Length - Math.Max(start(index), 0), Math.Min(start(index) + chunkSize - Math.Max(start(index), 0), chunkSize));
        return Enumerable.Range(0, count).Select(i => str.Substring(Math.Max(start(i), 0),end(i)));
    }

veya

    private static Func<bool, int, int, int, int, int> start = (remainingInFront, length, count, index, size) =>
        remainingInFront ? length - (count - index) * size : index * size;

    private static Func<bool, int, int, int, int, int, int> end = (remainingInFront, length, count, index, size, start) =>
        Math.Min(length - Math.Max(start, 0), Math.Min(start + size - Math.Max(start, 0), size));

    public static IEnumerable<string> Split(this string str, int chunkSize, bool remainingInFront)
    {
        var count = (int)Math.Ceiling(str.Length / (double)chunkSize);
        return Enumerable.Range(0, count).Select(i => str.Substring(
            Math.Max(start(remainingInFront, str.Length, count, i, chunkSize), 0),
            end(remainingInFront, str.Length, count, i, chunkSize, start(remainingInFront, str.Length, count, i, chunkSize))
        ));
    }

Tüm kenar kasaları AFAIK işlenir.

Console.WriteLine(string.Join(" ", "abc".Split(2, false))); // ab c
Console.WriteLine(string.Join(" ", "abc".Split(2, true))); // a bc
Console.WriteLine(string.Join(" ", "a".Split(2, true))); // a
Console.WriteLine(string.Join(" ", "a".Split(2, false))); // a

"Girdi boş bir dize" kenar durumu ne olacak? Ben Split gibi, bir IEnumerable tek bir boş dize içeren giriş ile dönmek için beklenir.
Nyerguds

3

Basit ve kısa:

// this means match a space or not a space (anything) up to 4 characters
var lines = Regex.Matches(str, @"[\s\S]{0,4}").Cast<Match>().Select(x => x.Value);

Neden kullanmıyorsunuz .?
marsze

3
static IEnumerable<string> Split(string str, int chunkSize)
{
   IEnumerable<string> retVal = Enumerable.Range(0, str.Length / chunkSize)
        .Select(i => str.Substring(i * chunkSize, chunkSize))

   if (str.Length % chunkSize > 0)
        retVal = retVal.Append(str.Substring(str.Length / chunkSize * chunkSize, str.Length % chunkSize));

   return retVal;
}

ChunkSize tarafından bölünemeyen giriş dizesi uzunluğunu doğru şekilde işler.

Kenar vakalarını (null veya boş giriş dizesi, chunkSize == 0) nazikçe işlemek için ek kod gerekebileceğini lütfen unutmayın.


2

Parçalanan dizenin tüm Unicode karakterleri desteklemesi gerekiyorsa önemli bir ipucu.

Dize gibi uluslararası karakterleri destekleyecekse 𠀋, System.Globalization.StringInfo sınıfını kullanarak dizeyi bölün. StringInfo'yu kullanarak, dizeyi metin öğelerinin sayısına göre bölebilirsiniz.

string internationalString = '𠀋';

String.LengthÖzellik, Unicode karakter sayısını değil, bu örnekteki Char nesnelerinin sayısını döndürdüğü için yukarıdaki dize 2 uzunluğuna sahiptir .


2

En İyi, En Kolay ve Genel Cevap :).

    string originalString = "1111222233334444";
    List<string> test = new List<string>();
    int chunkSize = 4; // change 4 with the size of strings you want.
    for (int i = 0; i < originalString.Length; i = i + chunkSize)
    {
        if (originalString.Length - i >= chunkSize)
            test.Add(originalString.Substring(i, chunkSize));
        else
            test.Add(originalString.Substring(i,((originalString.Length - i))));
    }

Son satırdaki uzunluğun hesaplanması gereksizdir, sadece Substringlength parametresi gerektirmeyen aşırı yükü kullanın originalString.Substring(i). Ayrıca çek >yerine kullanabilirsiniz >=.
Racil Hilan

@RacilHilan Kod değişikliklerini önerinizle test edip yanıtı güncelleyeceğim. Böyle iyi bir üne sahip birinin kodumu incelemek için zamanım var. :) Teşekkürler, Sandeep
Sandeep Kushwah

2

Şahsen ben çözümümü tercih ediyorum :-)

İşliyor:

  • Öbek boyutunun katı olan dize uzunlukları.
  • Öbek boyutunun katı OLMAYAN dize uzunlukları.
  • Öbek boyutundan daha küçük dize uzunlukları.
  • NULL ve boş dizeler (bir istisna atar).
  • 1'den küçük yığın boyutları (bir istisna atar).

Bir uzatma yöntemi olarak uygulanır ve önceden oluşturulacak parçaların sayısını hesaplar. Metin uzunluğunun kat olmaması durumunda daha kısa olması gerektiğinden, son yığını kontrol eder. Temiz, kısa, anlaşılması kolay ... ve çalışıyor!

    public static string[] Split(this string value, int chunkSize)
    {
        if (string.IsNullOrEmpty(value)) throw new ArgumentException("The string cannot be null.");
        if (chunkSize < 1) throw new ArgumentException("The chunk size should be equal or greater than one.");

        int remainder;
        int divResult = Math.DivRem(value.Length, chunkSize, out remainder);

        int numberOfChunks = remainder > 0 ? divResult + 1 : divResult;
        var result = new string[numberOfChunks];

        int i = 0;
        while (i < numberOfChunks - 1)
        {
            result[i] = value.Substring(i * chunkSize, chunkSize);
            i++;
        }

        int lastChunkSize = remainder > 0 ? remainder : chunkSize;
        result[i] = value.Substring(i * chunkSize, lastChunkSize);

        return result;
    }

2
List<string> SplitString(int chunk, string input)
{
    List<string> list = new List<string>();
    int cycles = input.Length / chunk;

    if (input.Length % chunk != 0)
        cycles++;

    for (int i = 0; i < cycles; i++)
    {
        try
        {
            list.Add(input.Substring(i * chunk, chunk));
        }
        catch
        {
            list.Add(input.Substring(i * chunk));
        }
    }
    return list;
}

1
Bu cevabı çok beğendim, ancak istisnai durumlar için istisnalar olduğu için belki denemek / yakalamak yerine ((i + 1) * chunk> = input.Length) kullanmalısınız.
nelsontruran

2

Bence bu basit bir cevap:

public static IEnumerable<string> Split(this string str, int chunkSize)
    {
        if(string.IsNullOrEmpty(str) || chunkSize<1)
            throw new ArgumentException("String can not be null or empty and chunk size should be greater than zero.");
        var chunkCount = str.Length / chunkSize + (str.Length % chunkSize != 0 ? 1 : 0);
        for (var i = 0; i < chunkCount; i++)
        {
            var startIndex = i * chunkSize;
            if (startIndex + chunkSize >= str.Length)
                yield return str.Substring(startIndex);
            else
                yield return str.Substring(startIndex, chunkSize);
        }
    }

Ve kenar kasaları kapsar.


2

Sorunun yaşında olduğunu biliyorum, ama burada bir Rx uygulaması var. O kolları length % chunkSize != 0kutunun sorunu Çıkış:

   public static IEnumerable<string> Chunkify(this string input, int size)
        {
            if(size < 1)
                throw new ArgumentException("size must be greater than 0");

            return input.ToCharArray()
                .ToObservable()
                .Buffer(size)            
                .Select(x => new string(x.ToArray()))
                .ToEnumerable();
        }

1

João'nun çözümünü biraz geliştirdim. Farklı yaptığım yöntemimde, diziyi kalan karakterlerle döndürmek isteyip istemediğinizi veya son karakterler istediğiniz yığın uzunluğuyla eşleşmiyorsa onları kısaltmak isteyip istemediğinizi belirtebilirsiniz, bence oldukça esnek ve kodu oldukça basit:

using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace SplitFunction
{
    class Program
    {
        static void Main(string[] args)
        {
            string text = "hello, how are you doing today?";
            string[] chunks = SplitIntoChunks(text, 3,false);
            if (chunks != null)
            {
                chunks.ToList().ForEach(e => Console.WriteLine(e));
            }

            Console.ReadKey();
        }

        private static string[] SplitIntoChunks(string text, int chunkSize, bool truncateRemaining)
        {
            string chunk = chunkSize.ToString(); 
            string pattern = truncateRemaining ? ".{" + chunk + "}" : ".{1," + chunk + "}";

            string[] chunks = null;
            if (chunkSize > 0 && !String.IsNullOrEmpty(text))
                chunks = (from Match m in Regex.Matches(text,pattern)select m.Value).ToArray(); 

            return chunks;
        }     
    }
}

1
    public static List<string> SplitByMaxLength(this string str)
    {
        List<string> splitString = new List<string>();

        for (int index = 0; index < str.Length; index += MaxLength)
        {
            splitString.Add(str.Substring(index, Math.Min(MaxLength, str.Length - index)));
        }

        return splitString;
    }

Sen, eh, MaxLength parametresini unuttun.
Nyerguds

1

Boyutu chunk ile eşit olmayan parçaları döndürmek için biraz değiştirildi

public static IEnumerable<string> Split(this string str, int chunkSize)
    {
        var splits = new List<string>();
        if (str.Length < chunkSize) { chunkSize = str.Length; }
        splits.AddRange(Enumerable.Range(0, str.Length / chunkSize).Select(i => str.Substring(i * chunkSize, chunkSize)));
        splits.Add(str.Length % chunkSize > 0 ? str.Substring((str.Length / chunkSize) * chunkSize, str.Length - ((str.Length / chunkSize) * chunkSize)) : string.Empty);
        return (IEnumerable<string>)splits;
    }

Emin geri-döküm kullanımını göremez o Listkadar IEnumerable; tüm yapmanız gereken, kullanmak isteyebileceğiniz Listeye özgü işlevleri gizlemektir. Sadece dönen hiçbir olumsuz yoktur List.
Nyerguds

1

Bana bunu kimin verdiğini hatırlayamıyorum, ama harika çalışıyor. Numaralandırılabilir türleri gruplara ayırmanın çeşitli yollarını hızlı bir şekilde test ettim. Kullanımı böyle olurdu ...

List<string> Divided = Source3.Chunk(24).Select(Piece => string.Concat<char>(Piece)).ToList();

Dahili kod şu şekilde görünecektir ...

#region Chunk Logic
private class ChunkedEnumerable<T> : IEnumerable<T>
{
    class ChildEnumerator : IEnumerator<T>
    {
        ChunkedEnumerable<T> parent;
        int position;
        bool done = false;
        T current;


        public ChildEnumerator(ChunkedEnumerable<T> parent)
        {
            this.parent = parent;
            position = -1;
            parent.wrapper.AddRef();
        }

        public T Current
        {
            get
            {
                if (position == -1 || done)
                {
                    throw new InvalidOperationException();
                }
                return current;

            }
        }

        public void Dispose()
        {
            if (!done)
            {
                done = true;
                parent.wrapper.RemoveRef();
            }
        }

        object System.Collections.IEnumerator.Current
        {
            get { return Current; }
        }

        public bool MoveNext()
        {
            position++;

            if (position + 1 > parent.chunkSize)
            {
                done = true;
            }

            if (!done)
            {
                done = !parent.wrapper.Get(position + parent.start, out current);
            }

            return !done;

        }

        public void Reset()
        {
            // per http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx
            throw new NotSupportedException();
        }
    }

    EnumeratorWrapper<T> wrapper;
    int chunkSize;
    int start;

    public ChunkedEnumerable(EnumeratorWrapper<T> wrapper, int chunkSize, int start)
    {
        this.wrapper = wrapper;
        this.chunkSize = chunkSize;
        this.start = start;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new ChildEnumerator(this);
    }

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

}
private class EnumeratorWrapper<T>
{
    public EnumeratorWrapper(IEnumerable<T> source)
    {
        SourceEumerable = source;
    }
    IEnumerable<T> SourceEumerable { get; set; }

    Enumeration currentEnumeration;

    class Enumeration
    {
        public IEnumerator<T> Source { get; set; }
        public int Position { get; set; }
        public bool AtEnd { get; set; }
    }

    public bool Get(int pos, out T item)
    {

        if (currentEnumeration != null && currentEnumeration.Position > pos)
        {
            currentEnumeration.Source.Dispose();
            currentEnumeration = null;
        }

        if (currentEnumeration == null)
        {
            currentEnumeration = new Enumeration { Position = -1, Source = SourceEumerable.GetEnumerator(), AtEnd = false };
        }

        item = default(T);
        if (currentEnumeration.AtEnd)
        {
            return false;
        }

        while (currentEnumeration.Position < pos)
        {
            currentEnumeration.AtEnd = !currentEnumeration.Source.MoveNext();
            currentEnumeration.Position++;

            if (currentEnumeration.AtEnd)
            {
                return false;
            }

        }

        item = currentEnumeration.Source.Current;

        return true;
    }

    int refs = 0;

    // needed for dispose semantics 
    public void AddRef()
    {
        refs++;
    }

    public void RemoveRef()
    {
        refs--;
        if (refs == 0 && currentEnumeration != null)
        {
            var copy = currentEnumeration;
            currentEnumeration = null;
            copy.Source.Dispose();
        }
    }
}
/// <summary>Speed Checked.  Works Great!</summary>
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> source, int chunksize)
{
    if (chunksize < 1) throw new InvalidOperationException();

    var wrapper = new EnumeratorWrapper<T>(source);

    int currentPos = 0;
    T ignore;
    try
    {
        wrapper.AddRef();
        while (wrapper.Get(currentPos, out ignore))
        {
            yield return new ChunkedEnumerable<T>(wrapper, chunksize, currentPos);
            currentPos += chunksize;
        }
    }
    finally
    {
        wrapper.RemoveRef();
    }
}
#endregion

1
class StringHelper
{
    static void Main(string[] args)
    {
        string str = "Hi my name is vikas bansal and my email id is bansal.vks@gmail.com";
        int offSet = 10;

        List<string> chunks = chunkMyStr(str, offSet);

        Console.Read();
    }

    static List<string> chunkMyStr(string str, int offSet)
    {


        List<string> resultChunks = new List<string>();

        for (int i = 0; i < str.Length; i += offSet)
        {
            string temp = str.Substring(i, (str.Length - i) > offSet ? offSet : (str.Length - i));
            Console.WriteLine(temp);
            resultChunks.Add(temp);


        }

        return resultChunks;
    }
}

Sen biraz kodunuzu iyileştirebilir: artım ifadesini kayması i += offSetsenin içine forifadesi.
JimiLoe

1

Modifiye (artık kabul herhangi olmayan boş stringve herhangi bir Pozitif chunkSize) Konstantin Spirin bireyin çözeltisi:

public static IEnumerable<String> Split(String value, int chunkSize) {
  if (null == value)
    throw new ArgumentNullException("value");
  else if (chunkSize <= 0)
    throw new ArgumentOutOfRangeException("chunkSize", "Chunk size should be positive");

  return Enumerable
    .Range(0, value.Length / chunkSize + ((value.Length % chunkSize) == 0 ? 0 : 1))
    .Select(index => (index + 1) * chunkSize < value.Length 
      ? value.Substring(index * chunkSize, chunkSize)
      : value.Substring(index * chunkSize));
}

Testler:

  String source = @"ABCDEF";

  // "ABCD,EF"
  String test1 = String.Join(",", Split(source, 4));
  // "AB,CD,EF"
  String test2 = String.Join(",", Split(source, 2));
  // "ABCDEF"
  String test3 = String.Join(",", Split(source, 123));

1
static List<string> GetChunks(string value, int chunkLength)
{
    var res = new List<string>();
    int count = (value.Length / chunkLength) + (value.Length % chunkLength > 0 ? 1 : 0);
    Enumerable.Range(0, count).ToList().ForEach(f => res.Add(value.Skip(f * chunkLength).Take(chunkLength).Select(z => z.ToString()).Aggregate((a,b) => a+b)));
    return res;
}

gösteri


Bu "chunkLenght" daha kısa olsa bile dize (post split) kalan tutar, teşekkürler
Jason Loki Smith

0

Diğer posterlerin cevaplarına dayanarak, bazı kullanım örnekleri ile birlikte:

public static string FormatSortCode(string sortCode)
{
    return ChunkString(sortCode, 2, "-");
}
public static string FormatIBAN(string iban)
{
    return ChunkString(iban, 4, "&nbsp;&nbsp;");
}

private static string ChunkString(string str, int chunkSize, string separator)
{
    var b = new StringBuilder();
    var stringLength = str.Length;
    for (var i = 0; i < stringLength; i += chunkSize)
    {
        if (i + chunkSize > stringLength) chunkSize = stringLength - i;
        b.Append(str.Substring(i, chunkSize));
        if (i+chunkSize != stringLength)
            b.Append(separator);
    }
    return b.ToString();
}

0

IX kitaplığındaki Arabellek uzantılarını kullanma

    static IEnumerable<string> Split( this string str, int chunkSize )
    {
        return str.Buffer(chunkSize).Select(l => String.Concat(l));
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.