文字列を特定のサイズのチャンクに分割する


218

文字列があるとしましょう:

string str = "1111222233334444"; 

この文字列をいくつかのサイズのチャンクに分割するにはどうすればよいですか?

たとえば、これを4のサイズに分割すると、文字列が返されます。

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

18
C#の標準の文字列操作関数が少ない労力と速度でこれを実行できるのに、なぜLINQまたは正規表現を使用するのですか?また、文字列の長さが奇数の場合はどうなりますか?
Ian Kemp、

7
「ループを避けたい」-なぜですか?
ミッチウィート

12
単純なループを使用すると、間違いなく最高のパフォーマンスが得られます。
グッファ09/09/20

4
nichesoftware.co.nz/blog/200909/linq-vs-loop-performanceは、配列に対するlinqと実際のループのかなり良い比較です。手動で作成したコードよりも速くlinqを見つけられるとは思えません。最適化するのが難しいランタイムデリゲートを呼び出し続けるからです。Linqはもっと楽しいですが:)
Blindy

2
LINQと正規表現のどちらを使用していても、ループはまだ存在しています。
Anton Tykhyy 2009

回答:


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パラメータのドロップイン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()関数の機能を理解する必要があります。これらの関数はBCLに何年も存在しているため、C#/。NETコードを記述するためにそれを理解しておく必要があるとは主張しません。
CodeMonkeyKing 2013年

6
トピックスターターはコメントで言ったStringLength % 4 will always be 0Linq理解が容易でない場合は、ループと利回りを使用する他の答えがあります。誰でも、彼女が最も気に入っているソリューションを自由に選択できます。あなたは自分のコードを答えとして投稿することができ、人々はそれに喜んで投票します。
Konstantin Spirin 2013年

3
Enumerable.Range(0、(str.Length + chunkSize-1)/ chunkSize).Select(i => str.Substring(i * chunkSize、Math.Min(str.Length-i * chunkSize、chunkSize)))
Stenペトロフ

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

しかし、OPはこれが必要ではないと明確に述べてます。少し長くて読みにくく、少し遅いです。KISSとYAGNIの精神で、最初のオプションを使用します。これは、おそらく最も効率的な実装であり、非常に短く、読みやすく、重要なことに、非準拠入力に対して例外をスローします。


4
頷く価値があります。ちょっと頭に釘を打ちます。彼は簡潔なsytnaxを探しており、あなたは(おそらく)より良いパフォーマンスも提供しています。

7
「static ... Chunk(this string str、int chunkSize){」にすると、「新しい」C#機能が1つ追加されます。次に、「1111222233334444」.Chunk(4)と記述できます。
MartinStettner、2009

1
@MartinStettner:これが一般的な操作であれば、確かにそれはまともな考えです。
イーモンネルボンヌ2009

後者のコードのみを含める必要があります。前者は、使用する前に文字列がチャンクサイズの倍数であることを理解してテストするか、文字列の残りを返さないことを理解する必要があります。
CodeMonkeyKing 2013年

OPの質問では、その機能が必要かどうかは明確にされていません。最初の解決策は、文字列を指定されたチャンクサイズに均等に分割できない場合を除いて、より単純で高速で確実に失敗します。「間違った」結果を返すことは悪いことだと私は同意しますが、それが何をするかではありません-例外をスローするだけなので、制限に耐えることができればそれを使用してもかまいません。
Eamon Nerbonne 2013年

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、2009

シンプルで効果的なソリューションの+1- i += chunkSize代わりに使用しましたが、これは私がそれを実行した方法です。
Ian Kemp、

おそらくマイナーな問題ですが、おそらくstr.Lengthループの外に出してローカル変数に入れるべきです。C#オプティマイザー配列の長さをインライン化できるかもしれませんが、記述されているコードはすべてのループでメソッド呼び出しを行うと思いstrます。サイズは決して変化しないため、効率的ではありません。
ダニエルプライデン2009

@ダニエル、そこにあなたのアイデアを入れてください。これが実行時に計算されないかどうかはわかりませんが、それは別の質問です;)
鳩の

@Danielがこれに戻ってきました。この最適化がコンパイラによって抽出されることを確認してください。

41

正規表現Linqを使用する:

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

これはもっと読みやすいと思いますが、それは個人的な意見です。ワンライナーにすることもできます:)。


7
パターンを@ "\ d {1,4}"に変更すると、任意の文字列長で機能します。:)
グッファ

3
+1これは他のソリューションより遅いですが、非常に読みやすいです。OPが数字を必要とするのか、任意の文字を必要とするのかは、はっきりしません。\d文字クラスをa に置き換えて.を指定するのが賢明でしょうRegexOptions.Singleline
イーモンネルボンヌ2009

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を超えてチェックを回避するために、空の文字列が1つだけ空の文字列の一部を返すことができるように、より論理的なようだ:if (str.Length == 0) yield return String.Empty; else { for... }
Nyerguds

つまり、通常のString.Splitが空の文字列を処理する方法です。空の文字列エントリを1つ返します。
Nyerguds

補足:使用例は間違っています。IEnumerable特に暗黙的にではなく、単に配列にキャストすることはできません。
Nyerguds

私は個人的にそのメソッドを呼び出すことのようなChunkify...それは私のものではありません、私はその名前を見てきたところ、私は覚えていないが、それは私には非常に素晴らしいと感じた
ケツァルコアトル

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が要素の順序を保持することは保証されていますか?
Konstantin Spirin

ToCharArrayですので不要stringですIEnumerable<char>
juharr 2015年

8

私は最近、これを達成するために何かを書く必要があったので、この問題の解決策を投稿したいと思いました。追加のボーナスとして、このソリューションの機能は、文字列を反対方向に分割する方法を提供し、前述のMarvin Pintoが前述したようにUnicode文字を正しく処理します。だから、ここにあります:

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 2012年

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

これは初期チェックを行うように見えますが、そうではありません。列挙型の列挙を開始するまで、エラーは発生しません。関数を2つの部分に分割する必要があります。最初の部分は引数のチェックを行い、次に、列挙を行う2番目のプライベート部分の結果を返します。
ErikE、2015年

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の作者の中には、ジョナサンスキートがいますが、ジョンスキートはいません。だから、もしかしてジョンスキート、または何を?;-)
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))
        ));
    }

私の知る限り、すべてのエッジケースが処理されます。

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と同様に、空の文字列を含む1つのエントリを持つ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 2017

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で割り切れない入力文字列の長さを正しく処理します。

エッジケース(nullまたは空の入力文字列、chunkSize == 0)を適切に処理するには、追加のコードが必要になる場合があることに注意してください。


2

チャンクされる文字列がすべてのUnicode文字をサポートする必要がある場合の重要なヒント。

文字列がなどの国際文字をサポートする場合は𠀋、System.Globalization.StringInfoクラスを使用して文字列を分割します。StringInfoを使用すると、テキスト要素の数に基づいて文字列を分割できます。

string internationalString = '𠀋';

String.Lengthプロパティは、Unicode文字の数ではなく、このインスタンスの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))));
    }

最後の行の長さの計算は冗長です。単純にSubstring、長さパラメーターを必要としないオーバーロードを使用してくださいoriginalString.Substring(i)。また、チェックの>代わりに使用することもでき>=ます。
Racil Hilan 2018

@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

私はジョアンのソリューションを少し構築しました。私が別の方法で行ったのは、私のメソッドで、残りの文字を含む配列を返すか、または終了文字が必要なチャンク長と一致しない場合にそれらを切り捨てるかどうかを実際に指定できることです。かなり柔軟で、コードはかなり単純です:

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

必ず私はバックキャスティングの使用を参照してくださいしないようListIEnumerable。必要なのは、使用したいList固有の関数を非表示にすることだけです。を返すだけではマイナス面はありませんList
Nyerguds

1

誰がこれをくれたのか思い出せませんが、うまくいきました。Enumerable型をグループに分割するいくつかの方法をスピードテストしました。使い方はこんな感じ...

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 2015

1

変更されました(現在、null以外および正の値をすべて受け入れます)stringchunkSizeコンスタンチン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」よりも短い文字列(ポストスプリット)の残りの部分を保持感謝
ジェイソン・ロキスミス

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.