Подтвердить что ты не робот

Разделить строку на более мелкие строки по переменной длины

Я хотел бы разбить String определенной переменной длины. Он должен ограничивать проверку, чтобы не взорваться, когда последний раздел строки не длиннее или длиннее длины. Ищете самую краткую (еще понятную) версию.

Пример:

string x = "AAABBBCC";
string[] arr = x.SplitByLength(3);
// arr[0] -> "AAA";
// arr[1] -> "BBB";
// arr[2] -> "CC"
4b9b3361

Ответ 1

Вам нужно использовать цикл:

public static IEnumerable<string> SplitByLength(this string str, int maxLength) {
    for (int index = 0; index < str.Length; index += maxLength) {
        yield return str.Substring(index, Math.Min(maxLength, str.Length - index));
    }
}

Альтернатива:

public static IEnumerable<string> SplitByLength(this string str, int maxLength) {
    int index = 0;
    while(true) {
        if (index + maxLength >= str.Length) {
            yield return str.Substring(index);
            yield break;
        }
        yield return str.Substring(index, maxLength);
        index += maxLength;
    }
}

2 nd альтернатива: (для тех, кто не может стоять while(true))

public static IEnumerable<string> SplitByLength(this string str, int maxLength) {
    int index = 0;
    while(index + maxLength < str.Length) {
        yield return str.Substring(index, maxLength);
        index += maxLength;
    }

    yield return str.Substring(index);
}

Ответ 2

Простая для понимания версия:

string x = "AAABBBCC";
List<string> a = new List<string>();
for (int i = 0; i < x.Length; i += 3)
{
    if((i + 3) < x.Length)
        a.Add(x.Substring(i, 3));
    else
        a.Add(x.Substring(i));
}

Хотя желательно, чтобы 3 был приятным const.

Ответ 3

Это не особенно лаконично, но я мог бы использовать метод расширения следующим образом:

public static IEnumerable<string> SplitByLength(this string s, int length)
{
    for (int i = 0; i < s.Length; i += length)
    {
        if (i + length <= s.Length)
        {
            yield return s.Substring(i, length);
        }
        else
        {
            yield return s.Substring(i);
        }
    }
}

Обратите внимание, что я возвращаю IEnumerable<string>, а не массив. Если вы хотите преобразовать результат в массив, используйте ToArray:

string[] arr = x.SplitByLength(3).ToArray();

Ответ 4

Вот что я сделал бы:

public static IEnumerable<string> EnumerateByLength(this string text, int length) {
    int index = 0;
    while (index < text.Length) {
        int charCount = Math.Min(length, text.Length - index);
        yield return text.Substring(index, charCount);
        index += length;
    }
}

Этот метод обеспечил бы отсроченное выполнение (что не имеет значения для неизменяемого класса, такого как string, но его стоит отметить).

Затем, если вы хотите, чтобы метод заполнил массив для вас, вы могли бы:

public static string[] SplitByLength(this string text, int length) {
    return text.EnumerateByLength(length).ToArray();
}

Причина, по которой я буду использовать имя EnumerateByLength, а не SplitByLength для метода "core", заключается в том, что string.Split возвращает string[], поэтому, на мой взгляд, приоритет для методов, имена которых начинаются с Split для возврата массивов.

Это только я, однако.

Ответ 5

Мое решение:

public static string[] SplitToChunks(this string source, int maxLength)
{
    return source
        .Where((x, i) => i % maxLength == 0)
        .Select(
            (x, i) => new string(source
                .Skip(i * maxLength)
                .Take(maxLength)
                .ToArray()))
        .ToArray();
}

Я скорее использую List<string> вместо string[].

Ответ 6

Используя Batch из MoreLinq, на .Net 4.0:

public static IEnumerable<string> SplitByLength(this string str, int length)
{
    return str.Batch(length, String.Concat);
}

В 3.5 Concat нужен массив, поэтому мы можем использовать Concat с ToArray или, new String:

public static IEnumerable<string> SplitByLength(this string str, int length)
{
    return str.Batch(length, chars => new String(chars.ToArray()));
}

Может показаться немного неинтуитивным, чтобы посмотреть на строку как на набор символов, поэтому можно было бы использовать манипуляции с строкой.

Ответ 7

UPD: использование некоторого Linq, чтобы сделать его фактически сжатым


static IEnumerable EnumerateByLength(string str, int len)
        {
            Match m = (new Regex(string.Format("^(.{{1,{0}}})*$", len))).Match(str);
            if (m.Groups.Count &lt= 1)
                return Empty;
            return (from Capture c in m.Groups[1].Captures select c.Value);
        }

Исходная версия:


        static string[] Empty = new string [] {};

        static string[] SplitByLength(string str, int len)
        {
            Regex r = new Regex(string.Format("^(.{{1,{0}}})*$",len));
            Match m = r.Match(str);
            if(m.Groups.Count &lt= 1)
                return Empty;

            string [] result = new string[m.Groups[1].Captures.Count];
            int ix = 0;
            foreach(Capture c in m.Groups[1].Captures)
            {
                result[ix++] = c.Value;
            }
            return result;
        }

Ответ 8

Еще один небольшой вариант (классический, но простой и прагматичный):

class Program
{
    static void Main(string[] args) {
        string msg = "AAABBBCC";

        string[] test = msg.SplitByLength(3);            
    }
}

public static class SplitStringByLength
{
    public static string[] SplitByLength(this string inputString, int segmentSize) {
        List<string> segments = new List<string>();

        int wholeSegmentCount = inputString.Length / segmentSize;

        int i;
        for (i = 0; i < wholeSegmentCount; i++) {
            segments.Add(inputString.Substring(i * segmentSize, segmentSize));
        }

        if (inputString.Length % segmentSize != 0) {
            segments.Add(inputString.Substring(i * segmentSize, inputString.Length - i * segmentSize));
        }

        return segments.ToArray();
    }
}

Ответ 9

    private string[] SplitByLength(string s, int d)
    {
        List<string> stringList = new List<string>();
        if (s.Length <= d) stringList.Add(s);
        else
        {
            int x = 0;
            for (; (x + d) < s.Length; x += d)
            {
                stringList.Add(s.Substring(x, d));
            }
            stringList.Add(s.Substring(x));
        }
        return stringList.ToArray();
    }

Ответ 10

    private void button2_Click(object sender, EventArgs e)
    {
        string s = "AAABBBCCC";
        string[] a = SplitByLenght(s,3);
    }

    private string[] SplitByLenght(string s, int split)
    {
        //Like using List because I can just add to it 
        List<string> list = new List<string>();

                    // Integer Division
        int TimesThroughTheLoop = s.Length/split;


        for (int i = 0; i < TimesThroughTheLoop; i++)
        {
            list.Add(s.Substring(i * split, split));

        }

        // Pickup the end of the string
        if (TimesThroughTheLoop * split != s.Length)
        {
            list.Add(s.Substring(TimesThroughTheLoop * split));
        }

        return list.ToArray();
    }

Ответ 11

У меня был странный сценарий, когда я сегментировал строку, а затем перегруппировал сегменты (т.е. обратные), прежде чем конкатенировать их, а затем мне пришлось изменить направление сегментации. Здесь обновление к принятому ответу @SLaks:

    /// <summary>
    /// Split the given string into equally-sized segments (possibly with a 'remainder' if uneven division).  Optionally return the 'remainder' first.
    /// </summary>
    /// <param name="str">source string</param>
    /// <param name="maxLength">size of each segment (except the remainder, which will be less)</param>
    /// <param name="remainderFirst">if dividing <paramref name="str"/> into segments would result in a chunk smaller than <paramref name="maxLength"/> left at the end, instead take it from the beginning</param>
    /// <returns>list of segments within <paramref name="str"/></returns>
    /// <remarks>Original method at https://stackoverflow.com/info/3008718/split-string-into-smaller-strings-by-length-variable </remarks>
    private static IEnumerable<string> ToSegments(string str, int maxLength, bool remainderFirst = false) {
        // note: `maxLength == 0` would not only not make sense, but would result in an infinite loop
        if(maxLength < 1) throw new ArgumentOutOfRangeException("maxLength", maxLength, "Should be greater than 0");
        // correct for the infinite loop caused by a nonsensical request of `remainderFirst == true` and no remainder (`maxLength==1` or even division)
        if( remainderFirst && str.Length % maxLength == 0 ) remainderFirst = false;

        var index = 0;
        // note that we want to stop BEFORE we reach the end
        // because if it exact we'll end up with an
        // empty segment
        while (index + maxLength < str.Length)
        {
            // do we want the 'final chunk' first or at the end?
            if( remainderFirst && index == 0 ) {
                // figure out remainder size
                var remainder = str.Length % maxLength;
                yield return str.Substring(index, remainder);

                index += remainder;
            }
            // normal stepthrough
            else {
                yield return str.Substring(index, maxLength);
                index += maxLength;
            }
        }

        yield return str.Substring(index);
    }//---  fn  ToSegments

(Я также исправил ошибку в исходной версии while, в результате которой был пустой сегмент, если maxLength==1)