Разделение строки на куски определенного размера

218

Предположим, у меня была строка:

string str = "1111222233334444"; 

Как я могу разбить эту строку на куски некоторого размера?

например, разбив это на размеры 4 вернет строки:

"1111"
"2222"
"3333"
"4444"
Джефф Меркадо
источник
18
Зачем использовать LINQ или регулярные выражения, когда стандартные функции C # для работы со строками могут делать это с меньшими усилиями и большей скоростью? Кроме того, что произойдет, если длина строки будет нечетным числом символов?
Ян Кемп
7
«Я бы хотел избежать петель» - почему?
Митч Уит
12
Использование простого цикла определенно дает лучшую производительность.
Гуффа
4
nichesoftware.co.nz/blog/200909/linq-vs-loop-performance - это довольно хорошее сравнение между linq и фактическим циклом над массивом. Я сомневаюсь, что вы когда-нибудь найдете linq быстрее, чем написанный вручную код, потому что он продолжает вызывать делегаты времени выполнения, которые трудно оптимизировать. Linq более веселый, хотя :)
Blindy
2
Используете ли вы LINQ или регулярные выражения, цикл все еще там.
Антон Тихий

Ответы:

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

Обратите внимание, что для изящной обработки краевых случаев может потребоваться дополнительный код ( nullили пустая входная строка chunkSize == 0, длина входной строки, не делимая на chunkSizeи т. Д.). Исходный вопрос не определяет никаких требований для этих крайних случаев, и в реальной жизни требования могут отличаться, поэтому они выходят за рамки этого ответа.

Константин Спирин
источник
3
@ Гарри, хороший улов! Это можно исправить с помощью вставного троичного выражения в параметре count подстроки. Что - то вроде: (i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize. Дополнительная проблема заключается в том, что эта функция не учитывает нулевую переменную str. Это может быть исправлено путем оборачивать все о возвращенном в другом тройном выражении: (str != null) ? ... : Enumerable.Empty<String>();.
Дрю Спикс
7
Это было близко, но в отличие от предыдущих 30 upvoters, я должен был изменить ограничение на количество петель в диапазоне от str.Length / chunkSizeдоdouble length = str.Length; double size = chunkSize; int count = (int)Math.Ceiling(length/size); return Enumerable.Range(0, count)...
щели
4
@KonstantinSpirin Я согласен, если код работал. Он обрабатывает только случай, когда строка кратна chunkSize, остальная часть строки теряется. Пожалуйста, исправьте. Также имейте в виду, что LINQ и его волшебство не так легко понять тому, кто просто хочет найти решение этой проблемы. Теперь человек должен понимать, что делают функции Enumerable.Range () и .Select (). Я не буду спорить, что вы должны понимать, как писать код на C # / .NET, поскольку эти функции были в BCL уже много лет.
CodeMonkeyKing
6
Автор темы сказал в комментариях, что StringLength % 4 will always be 0. Если Linqэто не так легко понять, то есть другие ответы, которые используют циклы и выходы. Любой может свободно выбрать решение, которое ему нравится больше всего. Вы можете опубликовать свой код как ответ, и люди с радостью проголосуют за него.
Константин Спирин
3
Enumerable.Range (0, (str.Length + chunkSize - 1) / chunkSize) .Select (i => str.Substring (i * chunkSize, Math.Min (str.Length - i * chunkSize, chunkSize)))
Стен Петров
135

В сочетании голубь + ответы Константина ...

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

Это будет работать для всех строк, которые могут быть разбиты на целое число фрагментов, и в противном случае будет выдано исключение.

Если вы хотите поддерживать строки любой длины, вы можете использовать следующий код:

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

Однако ОП явно заявил, что ему это не нужно; это немного длиннее и труднее читать, немного медленнее. В духе KISS и YAGNI я бы выбрал первый вариант: возможно, это наиболее эффективная реализация, и она очень короткая, удобочитаемая и, что важно, создает исключение для несоответствующего ввода.

Имон Нербонн
источник
4
+1 стоит кивнуть. вроде бьет по голове гвоздем. он ищет краткий ситнакс, и вы также даете (вероятно) лучшую производительность.
голубь
7
И если вы сделаете это "статическим ... Chunk (эта строка str, int chunkSize) {" у вас даже есть еще одна "новая" особенность C #. Затем вы можете написать «1111222233334444» .Chunk (4).
MartinStettner
1
@MartinStettner: Это, безусловно, неплохая идея, если это обычная операция.
Имон Нербонн
Вы должны включить только последний код. Первый требует, чтобы вы понимали и проверяли, чтобы строка была кратна размеру куска перед использованием, или понимали, что она не вернет остаток строки.
CodeMonkeyKing
Вопрос ОП не дает понять, нужна ли ему эта функциональность. Первое решение проще, быстрее и надежно дает сбой, за исключением случаев, когда строка не может быть равномерно разбита на указанный размер фрагмента. Я согласен, что возвращать «неправильные» результаты было бы плохо, но это не то, что он делает - это просто вызывает исключение, так что я бы согласился с его использованием, если вы можете жить с ограничением.
Имон Нербонн
56

Почему не петли? Вот кое-что, что сделало бы это довольно хорошо:

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

Я не знаю, как бы вы справились со случаем, когда строка не имеет множителя 4, но не говорите, что ваша идея невозможна, просто задаетесь вопросом о мотивации этого, если простой цикл for делает это очень хорошо? Очевидно, что вышеупомянутое можно очистить и даже добавить в качестве метода расширения.

Или, как упоминалось в комментариях, вы знаете, что это / 4 тогда

str = "1111222233334444";
for (int i = 0; i < stringLength; i += chunkSize) 
  {Console.WriteLine(str.Substring(i, chunkSize));} 
голубь
источник
1
Вы можете тянуть int chunkSize = 4за пределы петли. Он будет изменен только на последнем проходе.
Джон Феминелла
+1 за простое и эффективное решение - это то, как я бы это сделал, хотя я бы использовал i += chunkSizeвместо этого.
Ян Кемп
Вероятно, незначительный спор, но вы также должны вытащить str.Lengthиз цикла в локальную переменную. Оптимизатор C # может быть в состоянии встроить длину массива, но я думаю, что написанный код будет выполнять вызов метода в каждом цикле, что неэффективно, поскольку размер strникогда не изменяется.
Даниэль Приден
@ Даниэль, вставь свою идею туда. хотя я не уверен, что это не будет рассчитано во время выполнения, но это другой вопрос;)
dove
@Daniel, возвращаясь к этому, уверен, что эта оптимизация будет извлечена компилятором.
голубь
41

Используя регулярные выражения и Linq :

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

Я считаю это более читабельным, но это просто личное мнение. Это также может быть однострочник :).

Жоао Силва
источник
7
Измените шаблон на @ "\ d {1,4}", и он работает для любой длины строки. :)
Guffa
3
+1 Хотя это медленнее других решений, оно определенно очень читабельно. Мне не ясно, нужны ли для ОП цифры или произвольные символы; вероятно, было бы целесообразно заменить \dкласс символов на .и указать RegexOptions.Singleline.
Имон Нербонн
2
или просто Regex.Matches (s, @ "\ d {1,4}"). Select (m => m.Value) .ToList (); Я никогда не понимал смысл этого альтернативного синтаксиса, который служит только для того, чтобы скрыть, что мы используем методы расширения.
Даг
38

Это основано на решении @dove, но реализовано как метод расширения.

Преимущества:

  • Метод расширения
  • Крышки угловые
  • Разбивает строку на любые символы: цифры, буквы, другие символы

Код

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

использование

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

Модульные тесты удалены для краткости (см. Предыдущую версию )

алексей
источник
Интересное решение, но во избежание проверок на входе, выходящих за пределы нуля, кажется более логичным позволить пустой строке просто возвращать одну пустую строку:if (str.Length == 0) yield return String.Empty; else { for... }
Nyerguds
Я имею в виду, что именно так обычный String.Split обрабатывает пустые строки; он возвращает одну запись пустой строки.
Nyerguds
Примечание: ваш пример использования неверен. Вы не можете просто привести IEnumerableк массиву, особенно неявно.
Nyerguds
Лично мне нравится называть этот метод Chunkify... Это не мое, я не помню, где я видел это имя, но мне было очень приятно
quetzalcoatl
20

Как это для однострочника?

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

С этим регулярным выражением не имеет значения, если последний кусок меньше четырех символов, потому что он только когда-либо смотрит на символы позади него.

Я уверен, что это не самое эффективное решение, но мне просто нужно было его выбросить.

Алан Мур
источник
в случае target.Lenght % ChunckSize == 0этого возвращает дополнительную пустую строку, напримерList<string> result = new List<string>(Regex.Split("fooo", @"(?<=\G.{4})", RegexOptions.Singleline));
fubo
9

Это не красиво и не быстро, но работает, это одна строка и это 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();
Guffa
источник
Гарантируется ли, что GroupBy сохраняет порядок элементов?
Константин Спирин
ToCharArrayненужно, так как stringесть IEnumerable<char>.
juharr
8

Недавно мне пришлось написать что-то, что выполняет это на работе, поэтому я подумал, что опубликую свое решение этой проблемы. В качестве дополнительного бонуса, функциональность этого решения обеспечивает способ разделения строки в противоположном направлении, и он правильно обрабатывает символы Юникода, как ранее упоминал Марвин Пинто выше. Итак, вот оно:

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

Кроме того, вот ссылка на изображение с результатами выполнения этого кода: http://i.imgur.com/16Iih.png

Майкл Нельсон
источник
1
Я заметил проблему с этим кодом. У вас есть {str.ToString()}в конце вашего первого заявления IF. Вы уверены, что не имели в виду str.String? У меня была проблема с кодом выше, я сделал это изменение, и все заработало.
gunr2171
@ gunr2171 Похоже, что если str == null, эта строка также выдаст исключение NullReferenceException.
Джон Заброски
5

Это должно быть намного быстрее и эффективнее, чем использование LINQ или других подходов, используемых здесь.

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);
}
Джефф Меркадо
источник
Это похоже на раннюю проверку, но это не так. Вы не получите ошибку, пока не начнете перечислять перечислимые. Вам нужно разбить вашу функцию на две части, где первая часть выполняет проверку аргументов, а затем возвращает результаты второй, закрытой части, которая выполняет перечисление.
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;
}
HoloEd
источник
4

Вы можете использовать morelinq от Jon Skeet. Используйте Пакет как:

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

Это вернет 4 фрагмента для строки "1111222233334444". Если длина строки меньше или равна размеру куска Batch, строка вернется как единственный элементIEnumerable<string>

Для вывода:

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

и это даст:

1111
2222
3333
4444
Хабиб
источник
Среди авторов MoreLINQ я вижу Джонатана Скита , но не Джона Скита . Так что вы имели в виду на Джона Скита, или что? ;-)
Снаđошƒаӽ
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()));
}

и другой подход:

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

Шесть лет спустя о_О

Да просто так

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

или

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

AFAIK все крайние случаи обрабатываются.

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
Гербен Лимбург
источник
А как насчет крайнего случая «input is a empty string»? Я ожидаю, что, как и в случае с Split, вернуть IEnumerable с единственной пустой строкой, содержащей запись.
Nyerguds
3

Просто и коротко:

// 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);
Тоно Нам
источник
Почему бы не использовать .?
Марс
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.

Обратите внимание, что для изящной обработки краевых случаев может потребоваться дополнительный код (пустая или пустая строка ввода, chunkSize == 0).

Troncho
источник
2

Важный совет, если чанкованная строка должна поддерживать все символы Юникода.

Если строка должна поддерживать международные символы, такие как 𠀋, то разбейте строку, используя класс System.Globalization.StringInfo. Используя StringInfo, вы можете разделить строку по количеству текстовых элементов.

string internationalString = '𠀋';

Вышеуказанная строка имеет длину 2, потому что String.Lengthсвойство возвращает количество объектов Char в этом случае, а не количество символов Unicode.

Сет
источник
2

Лучший, самый простой и общий ответ :).

    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))));
    }
Сандип Кушва
источник
Вычисление длины в последней строке является излишним, просто используйте Substringперегрузку, которая не требует параметра длины originalString.Substring(i). Также вы можете использовать >вместо >=вашего чека.
Расил Хилан
@RacilHilan Я проверю изменения кода с вашим предложением и обновлю ответ. Я рад, что кто-то с такой хорошей репутацией получил время пересмотреть мой код. :) Спасибо, Сандип
Sandeep Kushwah
2

Лично я предпочитаю свое решение :-)

Он обрабатывает:

  • Длина строки, кратная размеру куска.
  • Длина строки НЕ кратна размеру куска.
  • Длина строки меньше размера чанка.
  • NULL и пустые строки (выдает исключение).
  • Размеры чанка меньше 1 (исключение).

Он реализован как метод расширения и рассчитывает количество порций, которые будут сгенерированы заранее. Он проверяет последний фрагмент, потому что, если длина текста не кратна, он должен быть короче. Чисто, коротко, легко понять ... и работает!

    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;
    }
Ibai
источник
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
Мне очень нравится этот ответ, но, возможно, вам следует использовать if ((i + 1) * chunk> = input.Length) вместо try / catch, поскольку исключения предназначены для исключительных случаев.
nelsontruran
2

Я думаю, что это прямой ответ:

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

И это охватывает крайние случаи.

Milad
источник
2

Я знаю, что вопрос лет, но вот реализация Rx. Это решает length % chunkSize != 0проблему из коробки:

   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

Я немного развил решение Жуана. Что я сделал по-другому, так это то, что в моем методе вы можете указать, хотите ли вы возвращать массив с оставшимися символами или вы хотите обрезать их, если конечные символы не соответствуют требуемой длине порции, я думаю, что это довольно гибко и Код довольно прост:

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;
    }
Рикин Патель
источник
Вы забыли параметр MaxLength.
Nyerguds
1

Немного изменен, чтобы вернуть части, размер которых не равен chunkSize

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;
    }
Абхишек Шреста
источник
Не уверен, что вижу применение обратного приведения Listк этому IEnumerable; все, что делает, это скрывает специфичные для List функции, которые вы, возможно, захотите использовать. Нет никаких недостатков в том, чтобы просто вернуть List.
Nyerguds
1

Я не могу вспомнить, кто дал мне это, но это прекрасно работает. Я быстро протестировал несколько способов разбить перечислимые типы на группы. Использование будет просто так ...

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

Код расширения будет выглядеть следующим образом ...

#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;
    }
}
Викас Бансал
источник
Вы можете немного улучшить свой код: сдвинуть выражение приращения i += offSetв свое forвыражение.
JimiLoe
1

Модифицированный (теперь он принимает любое ненулевое stringи любое положительное chunkSize) решение Константина Спирина :

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

тесты:

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

демонстрация

жэнь
источник
этот сохраняет остаток строки (после разбиения), даже если он короче «chunkLenght», спасибо
Джейсон Локи Смит
0

На основе ответов других постеров, а также некоторые примеры использования:

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

    static IEnumerable<string> Split( this string str, int chunkSize )
    {
        return str.Buffer(chunkSize).Select(l => String.Concat(l));
    }
bradgonesurfing
источник