문자열을 특정 크기의 덩어리로 나누기


218

문자열이 있다고 가정합니다.

string str = "1111222233334444"; 

이 문자열을 어떤 크기의 덩어리로 나눌 수 있습니까?

예를 들어 이것을 4 크기로 나누면 문자열이 반환됩니다.

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

18
C #의 표준 문자열 조작 함수가 적은 노력과 속도로이를 수행 할 수있는 경우 LINQ 또는 정규 표현식을 사용하는 이유는 무엇입니까? 또한 문자열의 길이가 홀수 인 경우 어떻게됩니까?
Ian Kemp

7
"루프를 피하고 싶습니다"-왜?
Mitch Wheat

12
간단한 루프를 사용하면 최고의 성능을 얻을 수 있습니다.
Guffa September

4
nichesoftware.co.nz/blog/200909/linq-vs-loop-performance 는 배열에서 linq와 실제 루핑을 비교해 보면 매우 좋습니다. linq가 수동으로 작성된 코드보다 더 빨리 linq를 찾지 못할 것이라고 생각합니다. 최적화하기 어려운 런타임 대리자를 계속 호출하기 때문입니다. LINQ는 :)하지만 더 재미
Blindy

2
LINQ를 사용하든 regexe를 사용하든 루프는 여전히 존재합니다.
Anton Tykhyy

답변:


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
@Harry Good catch! 이는 하위 문자열의 count 매개 변수에 대한 3 항 드롭 인 식으로 해결할 수 있습니다. 같은 것 : (i * chunkSize + chunkSize <= str.Length) ? chunkSize : str.Length - i * chunkSize. 또 다른 문제는이 함수가 str이 null임을 설명하지 않는다는 것입니다. 전체 return 문을 다른 삼항 식으로 래핑하여이 문제를 해결할 수 있습니다 (str != null) ? ... : Enumerable.Empty<String>();.
Drew Spickes

7
이는 듯했으나 이전 30 upvoters는 달리, 나는 범위의 루프 카운트 제한을 변경했습니다 str.Length / chunkSizedouble 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이해하기 쉽지 않은 경우 루프와 수율을 사용하는 다른 답변이 있습니다. 누구나 가장 좋아하는 솔루션을 자유롭게 선택할 수 있습니다. 코드를 답변으로 게시하면 사람들이 행복하게 투표합니다.
Konstantin Spirin

3
열거 가능 범위 (0, (str. 길이 + chunkSize-1) / chunkSize). (i => str.Substring (i * chunkSize, Math.Min (str.Length-i * chunkSize, chunkSize)))
Sten을 선택합니다. Petrov

135

dove + Konstatin의 답변의 조합에서 ...

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

그러나 OP는 명시 적으로 이것을 필요로 하지 않는다고 명시했다 . 읽기가 다소 길고 어렵고 약간 느립니다. KISS와 YAGNI의 정신으로, 나는 첫 번째 옵션을 사용할 것입니다 : 아마도 가장 효율적인 구현 일 것입니다. 매우 짧고 읽기 쉽고 중요하지 않은 입력에 대한 예외가 발생합니다.


4
끄덕임없이 +1 가치가 있습니다. 좀 머리에 못을 박았다. 간결한 sytnax를 찾고 있으며 성능을 향상시킬 수도 있습니다.
비둘기

7
그리고 "static ... Chunk (이 문자열 str, int chunkSize) {"로 만들면 "새로운"C # 기능이 하나 더 있습니다. 그런 다음 "1111222233334444".Chunk (4)를 쓸 수 있습니다.
MartinStettner

1
@ MartinStettner : 이것이 일반적인 작업이라면 확실히 괜찮은 아이디어입니다.
Eamon Nerbonne

후자의 코드 만 포함해야합니다. 전자는 문자열을 사용하기 전에 여러 청크 크기의 문자열을 이해하고 테스트하거나 나머지 문자열을 리턴하지 않음을 이해해야합니다.
CodeMonkeyKing

OP의 질문은 그가 그 기능이 필요한지 명확하지 않습니다. 첫 번째 솔루션은 문자열을 지정된 청크 크기로 균등하게 분할 할 수없는 경우를 제외하고는 간단하고 빠르며 안정적으로 실패합니다. "잘못된"결과를 반환하는 것은 좋지 않지만 그 결과가 아니라는 점에 동의합니다. 예외가 발생하므로 제한에 따라 살 수 있다면 사용하는 것이 좋습니다.
Eamon Nerbonne

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루프 바깥쪽으로 당길 수 있습니다 . 최종 패스에서만 수정됩니다.
John Feminella

간단하고 효과적인 해결책을 위해 +1-이것이 i += chunkSize대신 사용했을지라도 이것이 내가 한 일 입니다.
이안 켐프

아마도 사소한 경감이지만 아마도 str.Length루프에서 로컬 변수로 가져와야 할 것입니다. 는 C # 최적화 할 수 있습니다 인라인 배열 길이로 할 수 있지만이 크기 때문에, 효율적이지 않습니다 모든 루프에 메서드 호출을 할 것입니다 작성된 코드 생각 str절대 변경합니다.
다니엘 프 라이덴

@Daniel, 아이디어를 거기에 넣으십시오. 비록 이것이 런타임에 계산되지 않을지는 모르겠지만, 그것은 또 다른 질문입니다;)
비둘기

@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 다른 솔루션보다 속도는 느리지 만 읽기 쉽습니다. OP에 숫자 또는 임의의 문자가 필요한지 여부는 명확하지 않습니다. 아마도 대체하는 것이 현명 할 것 \dA를 문자 클래스를 .하고 지정합니다 RegexOptions.Singleline.
Eamon Nerbonne

2
또는 그냥 Regex.Matches (s, @ "\ d {1,4}"). Select (m => m.Value) .ToList (); 나는 우리가 확장 메소드를 사용하고 있다는 것을 혼란스럽게하는이 대안 구문의 요점을 결코 얻지 못했습니다.
Dag

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

간결성을 위해 단위 테스트가 제거되었습니다 ( 이전 개정 참조 ).


흥미로운 해결책이지만 입력에서 null 이외의 검사를 피하기 위해 빈 문자열이 단일 빈 문자열 부분 만 반환하도록하는 것이 더 논리적 인 것 같습니다.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));

이 정규 표현식을 사용하면 마지막 청크가 4 자 미만인지 여부는 중요하지 않습니다. 왜냐하면 그 뒤에 나오는 문자 만 볼 수 있기 때문입니다.

나는 이것이 가장 효율적인 해결책은 아니라고 확신하지만 거기에서 그것을 던져야했습니다.


이 경우 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();

GroupBy가 요소의 순서를 유지한다고 보장됩니까?
콘스탄틴 스피 린

ToCharArray이후 불필요 stringIS IEnumerable<char>.
juharr

8

나는 최근에 직장에서 이것을 달성하는 것을 작성해야 했으므로이 문제에 대한 해결책을 게시 할 것이라고 생각했습니다. 추가로이 솔루션의 기능은 문자열을 반대 방향으로 분할하는 방법을 제공하며 앞에서 Marvin Pinto가 언급 한 유니 코드 문자를 올바르게 처리합니다. 그래서 여기 있습니다 :

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 2016 년

@ gunr2171 str == null 인 경우 해당 줄에도 NullReferenceException이 발생합니다.
John Zabroski

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

4

Jon Skeet의 morelinq 를 사용할 수 있습니다 . 배치를 다음 과 같이 사용하십시오 .

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의 저자 중 Jonathan Skeet 은 보지만 Jon Skeet은 없습니다 . 그래서 당신은 의미 않았다 존 소총, 또는 무엇을? ;-)
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()));
}

그리고 또 다른 접근법 :

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

6 년 후 o_O

으니까

    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

"입력은 빈 문자열입니다"엣지 케이스는 어떻습니까? 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);

왜 사용하지 .않습니까?
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로 나눌 수없는 입력 문자열 길이를 올바르게 처리합니다.

가장자리 케이스를 정상적으로 처리하려면 추가 코드가 필요할 수 있습니다 (널 또는 빈 입력 문자열, chunkSize == 0).


2

청크되는 문자열이 모든 유니 코드 문자를 지원해야하는 경우 중요한 팁입니다.

문자열이 같은 국제 문자를 지원 𠀋하는 경우 System.Globalization.StringInfo 클래스를 사용하여 문자열을 분할하십시오. StringInfo를 사용하면 텍스트 요소 수에 따라 문자열을 분할 할 수 있습니다.

string internationalString = '𠀋';

이 문자열의 길이 String.Length는 유니 코드 문자 수가 아닌이 인스턴스의 Char 객체 수를 반환 하므로 길이는 2 입니다.


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

마지막 줄에서 길이를 계산하는 것은 불필요합니다. 단순히 Substringlength 매개 변수가 필요없는 과부하를 사용하십시오 originalString.Substring(i). 또한 수표 >대신에 사용할 수 있습니다 >=.
Racil Hilan

@RacilHilan 귀하의 제안으로 코드 변경 사항을 테스트하고 답변을 업데이트하겠습니다. 좋은 평판을 가진 사람이 내 코드를 검토 할 시간이있어서 기쁩니다. :) 감사합니다, Sandeep
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;
    }

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
나는이 답변을 많이 좋아하지만 예외적 인 예외로 인해 try / catch 대신 if ((i + 1) * chunk> = input.Length)를 사용해야합니다.
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);
        }
    }

그리고 그것은 가장자리 경우를 다룹니다.


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

João의 솔루션을 약간 구축했습니다. 내가 다르게 한 것은 내 방법에서 실제로 나머지 문자로 배열을 반환할지 또는 끝 문자가 필요한 청크 길이와 일치하지 않는 경우자를 지 여부를 지정할 수 있습니다. 매우 유연하고 코드는 매우 간단합니다.

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.
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) 콘스탄틴 Spirin 의 용액 :

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"보다 짧아도 나머지 줄 (포스트 스플릿)을 유지합니다. 감사합니다
Jason Loki Smith

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));
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.