2015-12-23 21 views
7

以下のコードを使用して文字列を分割しましたが、時間がかかります。巨大なテキストを小さな塊に分割する最速の方法

using (StreamReader srSegmentData = new StreamReader(fileNamePath)) 
{ 
    string strSegmentData = ""; 
    string line = srSegmentData.ReadToEnd(); 
    int startPos = 0; 

    ArrayList alSegments = new ArrayList(); 
    while (startPos < line.Length && (line.Length - startPos) >= segmentSize) 
    { 
     strSegmentData = strSegmentData + line.Substring(startPos, segmentSize) + Environment.NewLine; 
     alSegments.Add(line.Substring(startPos, segmentSize) + Environment.NewLine); 
     startPos = startPos + segmentSize; 
    } 
} 

私の固定サイズの小さな塊あなたはチャンクサイズで何を意味するかを定義する必要があり、すべての

+0

'String.SのStringReaderクラスを使用する必要がありますplitは1つのオプションになる可能性があります –

+0

これは役立つかもしれません:http://stackoverflow.com/questions/568968/does-any-one-know-of-a-faster-method-to-do-string-split – MusicLovingIndianGirl

+0

私たちは、スプリットを使用する特定の文字を持って、ちょうどサイズ(文字数)に基づいて文字列を分離する必要があります –

答えて

12

初に文字列を分割する別の方法を提案してください。 コードユニットの固定番号ののチャンクを意味する場合、実際のアルゴリズムは遅くなる可能性がありますが動作します。それはあなたが意図するものではありませんし、場合、あなたが実際に文字の固定数でチャンクを意味し、それは壊れています。私はこのコードレビューポストで同様の問題について議論しました:Split a string into chunks of the same length次に、ここでは関連する部分のみを繰り返します。

  1. 1文字以上でエンコードされた:あなたはCharしかしStringの上に分割している

    • はUTF-16は、少なくとも、3例は、中にあなたが壊れ文字列を生成することが、その後エンコードされています1つのコードユニット。その文字のUnicodeコードポイントは2つのUTF-16コードユニットとしてエンコードされ、各コードユニットは2つの異なるスライスになります(両方の文字列は無効)。

    • 1文字を複数のコードポイントによって構成されています。あなたは、2つの別々のUnicodeコードポイント(例えば、漢字)によって作られた文字を扱っています。
    • 1文字には組み合わせ文字または修飾子が含まれています。これは、あなたが考えることよりも一般的である。例えばUnicodeにはとUnicodeなどU + 02BC MODIFIER LETTER APOSTROPHEとして修飾àビルドに使用GRAVEのACCENTを組み合わせU + 0300のような文字を組み合わせます。
  • 定義プログラミング言語用および人間用の文字しかしそれはこのケースである2/3のUnicodeコードポイントによって作られている単一の文字であるDZスロバキアで、たとえば、かなり異なっています2/3 UTF-16コードユニット、次に"dž".Length > 1。これとその他の文化に関する問題の詳細How can I perform a Unicode aware character by character comparison?に関する詳細
  • 合字が存在します。 1つの合字が1つのコードポイントであると仮定すると、それは単一のグリフとして扱われますが、それは2つの文字を表します。この場合、どうしたらいいですか? 文字のの一般的な定義では、この単語が使用されている分野に応じての意味が異なるの意味を持つため、かなり曖昧な場合があります。すべて(おそらく)すべてを正しく処理することはできませんが、いくつかの制約とドキュメントコードの動作を設定する必要があります。
  • 一つの提案(未検証)の実装は、このことがあります

    public static IEnumerable<string> Split(this string value, int desiredLength) 
    { 
        var characters = StringInfo.GetTextElementEnumerator(value); 
        while (characters.MoveNext()) 
         yield return String.Concat(Take(characters, desiredLength)); 
    } 
    
    private static IEnumerable<string> Take(TextElementEnumerator enumerator, int count) 
    { 
        for (int i = 0; i < count; ++i) 
        { 
         yield return (string)enumerator.Current; 
    
         if (!enumerator.MoveNext()) 
          yield break; 
        } 
    } 
    

    (あなたは私が短いコードを維持しようとした参照と列挙を使用してクリアすることができたように)が、これは、大きなファイルのために、スピードに最適化されていないです、それはあなたの実装よりも優れた性能を発揮します(理由については次の段落を参照してください)。あなたは巨大なArrayListを構築している

    • 結果を保持するために(?!):というあなたのコードノートについて

      。また、このようにして、ArrayListを複数回サイズ変更することもできます(指定された入力サイズとチャンクサイズ、最終サイズが分かっていても)。

    • strSegmentDataを複数回再構築した場合、文字を蓄積する必要がある場合は、StringBuilderを使用する必要があります。そうしないと、新しい文字列が割り当てられ、古い値がコピーされます(遅くなり、ガベージコレクタにも負荷がかかります)。その後、

    は(リンク参照コードレビューのポスト、はるかに高速バージョンでは特にHeslacher's implementation)より高速な実装があり、あなたが正しくUnicodeを処理する必要がない場合は(あなたは必ずあなたはUS ASCII文字のみを管理しています)かなりreadable implementation from Jon Skeetもあります(コードをプロファイリングした後に、適切なサイズの出力リストをあらかじめ割り当てておくと大きなファイルのパフォーマンスが向上する可能性があります)。私はここでコードを繰り返していないし、リンク先の記事を参照してください。あなたはメモリ全体の巨大なファイルを読む必要はありません個人の特定に

    、一度にN文字を解析/読むことができます(ディスクアクセスについてはあまり心配しないで、I/Oがバッファリングされます)。パフォーマンスはわずかに低下しますが、メモリ使用量が大幅に向上します。あるいは、行ごとに読むことができます(クロスラインチャンクの処理を管理します)。以下は

    0

    は、あなたの質問や、コードの私の分析である(コメントを読む)

    using (StreamReader srSegmentData = new StreamReader(fileNamePath)) 
    { 
        string strSegmentData = ""; 
        string line = srSegmentData.ReadToEnd(); // Why are you reading this till the end if it is such a long string? 
        int startPos = 0; 
    
        ArrayList alSegments = new ArrayList(); // Better choice would be to use List<string> 
        while (startPos < line.Length && (line.Length - startPos) >= segmentSize) 
        { 
         strSegmentData = strSegmentData + line.Substring(startPos, segmentSize) + Environment.NewLine; // Seem like you are inserting linebreaks at specified interval in your original string. Is that what you want? 
         alSegments.Add(line.Substring(startPos, segmentSize) + Environment.NewLine); // Why are you recalculating the Substring? Why are you appending the newline if the aim is to just "split" 
         startPos = startPos + segmentSize; 
        } 
    } 
    

    仮定のすべての種類を作り、以下の私は長い文字列を分割するために推薦するコードです。それは、あなたがサンプルでやっていることをするための単なるクリーンな方法です。これを最適化することはできますが、探している速度を確認することはできません。

    static void Main(string[] args) { 
        string fileNamePath = "ConsoleApplication1.pdb"; 
        var segmentSize = 32; 
    
        var op = ReadSplit(fileNamePath, segmentSize); 
        var joinedSTring = string.Join(Environment.NewLine, op); 
    } 
    
    static List<string> ReadSplit(string filePath, int segmentSize) { 
        var splitOutput = new List<string>(); 
        using (var file = new StreamReader(filePath, Encoding.UTF8, true, 8 * 1024)) { 
         char []buffer = new char[segmentSize]; 
         while (!file.EndOfStream) { 
          int n = file.ReadBlock(buffer, 0, segmentSize); 
          splitOutput.Add(new string(buffer, 0, n)); 
         } 
        } 
    
        return splitOutput; 
    } 
    

    私のバージョンではパフォーマンステストを行っていませんが、あなたのバージョンよりも速いと思います。

    また、出力をどのように使用するかはわかりませんが、I/Oを実行するときの最適化は非同期呼び出しを使用することです。大取り扱うときや(読みやすさと複雑さのコストで)良い最適化がstring

    • あなたはファイル
    • を読みながら文字エンコーディングの問題に対処する必要がありますchar[]

      注ことに固執することですあなたは既にメモリやファイルの読み込み中に長い文字列がちょうどデモに含まれている場合は、あなたの代わりにStreamReaderクラス

    関連する問題