2009-10-27 24 views
10

Może podstawowe pytanie, ale powiedzmy, że mam ciąg znaków o długości 2000 znaków, muszę podzielić ten ciąg na maksymalnie 512 fragmentów znaków.Podziel ciąg w 512 kawałkach znaków

Czy jest jakiś fajny sposób, na przykład pętla?

+0

Czy na pewno chcesz char ** 512 ** kawałki? Ponieważ różni się od 512 ** bajtów **, co jest bardziej powszechnym ograniczeniem. –

+1

@Henk: Z drugiej strony, dzielenie * tekstu * na porcje na podstawie * bajtów * byłoby dość dziwne - wyniki zależą od kodowania. –

+0

Jon, tak, często występuje problem podczas ponownego składania tekstu. Ale niektóre kanały I/O działają w blokach 512-bajtowych. –

Odpowiedz

20

coś takiego:

private IList<string> SplitIntoChunks(string text, int chunkSize) 
{ 
    List<string> chunks = new List<string>(); 
    int offset = 0; 
    while (offset < text.Length) 
    { 
     int size = Math.Min(chunkSize, text.Length - offset); 
     chunks.Add(text.Substring(offset, size)); 
     offset += size; 
    } 
    return chunks; 
} 

Albo po prostu iteracyjne nad:

private IEnumerable<string> SplitIntoChunks(string text, int chunkSize) 
{ 
    int offset = 0; 
    while (offset < text.Length) 
    { 
     int size = Math.Min(chunkSize, text.Length - offset); 
     yield return text.Substring(offset, size); 
     offset += size; 
    } 
} 

pamiętać, że ta dzieli się na kawałki UTF-16 jednostek kodu, który nie jest całkiem tak samo jak rozszczepienie w kawałki punktów kodu Unicode, które z kolei mogą nie być tym samym, co fragmenty glifów.

+0

dammit Jon you pobili mnie do tego i użyłem twojej implementacji też ... –

+0

Wiele się z tego nauczyłem. Zaakceptuj i daj +1. Bardzo dobrze! – janhartmann

+0

Algorytm ten (i jego kompatybilność z Unicode) omówiono również w recenzji kodu: [Podziel ciąg na fragmenty o tej samej długości] (http://codereview.stackexchange.com/a/111925/13424). –

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

źródło: Splitting a string into chunks of a certain size

+0

+1 za kreatywność, -1 za wydajność i czytelność – Foxfire

+9

Nie udało się dostarczyć ostatecznego fragmentu w tym przypadku. –

-1

Coś takiego?

Calculate eachLength = StringLength/WantedCharLength 
Then for (int i = 0; i < StringLength; i += eachLength) 
SubString (i, eachLength); 
+2

Czy to C#? .... – Abel

1

będę śmiał zapewnić więcej LINQified wersję rozwiązania Jona, w oparciu o fakt, że typ string realizuje IEnumerable<char>:

private IList<string> SplitIntoChunks(string text, int chunkSize) 
{ 
    var chunks = new List<string>(); 
    int offset = 0; 
    while(offset < text.Length) { 
     chunks.Add(new string(text.Skip(offset).Take(chunkSize).ToArray())); 
     offset += chunkSize; 
    } 
    return chunks; 
} 
+1

Rozważyłem to - szczególnie, że MoreLINQ zapewnia ładną metodę podziału na tego typu rzeczy. Jednak efektywność tego byłaby absolutnie okropna :( –

+0

Dobrze jest wiedzieć, że ponieważ używam LINQ do wszystkiego ... – Konamiman

+0

btw String, nie ma metody rozszerzenia dla "Skip", co musiałbyś zrobić ToCharArray najpierw –

3

użyciu realizację Jona i plastyczności słowa kluczowego.

IEnumerable<string> Chunks(string text, int chunkSize) 
{ 
    for (int offset = 0; offset < text.Length; offset += chunkSize) 
    { 
     int size = Math.Min(chunkSize, text.Length - offset); 
     yield return text.Substring(offset, size); 
    } 
} 
+0

Ciekawe użycie dla vs mój czas ... Jestem próbując zdecydować, który jest łatwiejszy do odczytania. Nie potrzebujesz przerwy na plonowanie na końcu, btw. –

+0

Zrobiłem sobie prawo do naprawienia zbędnego limitu rentowności jako @Jon wspomniany –

+0

dzięki, @LouisRhys :) –

3

Chociaż to pytanie ma na razie akceptowaną odpowiedź, oto krótka wersja z pomocą wyrażeń regularnych. Puryści mogą tego nie lubić (co zrozumiałe), ale kiedy potrzebujesz szybkiego rozwiązania i jesteś przydatny w przypadku wyrażeń regularnych, to może być to. Wydajność jest dość dobra, zaskakująco:

string [] split = Regex.Split(yourString, @"(?<=\G.{512})"); 

Co robi? Negatywne spojrzenie do tyłu i zapamiętanie ostatniej pozycji z \G. Będzie także przechwytywał ostatni bit, nawet jeśli nie można go podzielić przez 512.

+0

Przydatny do kontroli długich łańcuchów w okienku bezpośrednim. – Dialecticus

+1

Dlaczego nie? Jest krótki, szybki i wyraźny (jeśli znasz regexes, osobiście musiałem go przeczytać i przetestować 3 razy). Dobra wersja! –

1

Większość odpowiedzi może mieć tę samą wadę. Biorąc pod uwagę pusty tekst, nic nie przyniosą. My (I) oczekujemy przynajmniej powrotu do tego pustego ciągu znaków (to samo zachowanie co split na znaku nie w ciągu znaków, który zwróci jeden element: ten podany ciąg)

, więc powinniśmy wykonać pętlę co najmniej raz czasy (na podstawie kodu Jona):

IEnumerable<string> SplitIntoChunks (string text, int chunkSize) 
{ 
    int offset = 0; 
    do 
    { 
     int size = Math.Min (chunkSize, text.Length - offset); 
     yield return text.Substring (offset, size); 
     offset += size; 
    } while (offset < text.Length); 
} 

lub za pomocą for (Edited: po bawiąc się trochę bardziej z tego, znalazłem lepszy sposób obsłużyć sprawę chunkSize większy niż tekst):

IEnumerable<string> SplitIntoChunks (string text, int chunkSize) 
{ 
    if (text.Length <= chunkSize) 
     yield return text; 
    else 
    { 
     var chunkCount = text.Length/chunkSize; 
     var remainingSize = text.Length % chunkSize; 

     for (var offset = 0; offset < chunkCount; ++offset) 
      yield return text.Substring (offset * chunkSize, chunkSize); 

     // yield remaining text if any 
     if (remainingSize != 0) 
      yield return text.Substring (chunkCount * chunkSize, remainingSize); 
    } 
} 

To może być również używany z do/while;)

0

Typowa metoda rozszerzenie:

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

public static class IEnumerableExtensions 
{ 
    public static IEnumerable<IEnumerable<T>> SplitToChunks<T> (this IEnumerable<T> coll, int chunkSize) 
    { 
    int skipCount = 0; 
    while (coll.Skip (skipCount).Take (chunkSize) is IEnumerable<T> part && part.Any()) 
    { 
     skipCount += chunkSize; 
     yield return part; 
    } 
    } 
} 

class Program 
{ 
    static void Main (string[] args) 
    { 
    var col = Enumerable.Range(1,1<<10); 
    var chunks = col.SplitToChunks(8); 

    foreach (var c in chunks.Take (200)) 
    { 
     Console.WriteLine (string.Join (" ", c.Select (n => n.ToString ("X4")))); 
    } 

    Console.WriteLine(); 
    Console.WriteLine(); 

    "Split this text into parts that are fifteen characters in length, surrounding each part with single quotes and output each into the console on seperate lines." 
     .SplitToChunks (15) 
     .Select(p => $"'{string.Concat(p)}'") 
     .ToList() 
     .ForEach (p => Console.WriteLine (p)); 

    Console.ReadLine(); 
    } 
}