Разбиение строки на куски определенного размера - PullRequest
192 голосов
/ 20 сентября 2009

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

string str = "1111222233334444"; 

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

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

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

Ответы [ 31 ]

201 голосов
/ 20 сентября 2009
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 и т. Д.). Исходный вопрос не определяет никаких требований для этих крайних случаев, и в реальной жизни требования могут отличаться, поэтому они выходят за рамки этого ответа.

123 голосов
/ 20 сентября 2009

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

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 я бы выбрал первый вариант: возможно, это наиболее эффективная реализация, и она очень короткая, удобочитаемая и, что важно, создает исключение для несоответствующего ввода.

49 голосов
/ 20 сентября 2009

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

        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));} 
36 голосов
/ 20 сентября 2009

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

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

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

30 голосов
/ 20 января 2012

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

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

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

код

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

Для краткости удалены юнит-тесты (см. предыдущая версия )

19 голосов
/ 21 сентября 2009

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

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

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

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

8 голосов
/ 20 сентября 2009

Это не красиво и не быстро, но работает, это однострочный и 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();
7 голосов
/ 22 января 2012

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

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

5 голосов
/ 18 октября 2011

Это должно быть намного быстрее и эффективнее, чем использование 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);
}
4 голосов
/ 10 марта 2010
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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...