2015-12-23 24 views
7

Ho usato il codice seguente per dividere la stringa, ma ci vuole molto tempo.Il modo più veloce per dividere un testo enorme in blocchi più piccoli

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

Si prega di suggerire me un modo alternativo per dividere la stringa in piccoli pezzi di dimensioni fisse

+0

'String.S. plit' potrebbe essere un'opzione –

+0

Questo potrebbe aiutare: http://stackoverflow.com/questions/568968/does-any-one-know-of-a-faster-method-to-do-string-split – MusicLovingIndianGirl

+0

noi don ' Ho un carattere specifico per usare Split, devi solo separare la stringa in base alla dimensione (numero di caratteri) –

risposta

12

Prima di tutto è necessario definire cosa si intende con la dimensione pezzo. Se si intendono i blocchi con un numero fisso di unità di codice , il proprio algoritmo potrebbe essere lento ma funziona. Se non è quello che intendi e in realtà intendi blocchi con un numero fisso di caratteri, allora è rotto. Ho discusso un problema simile in questo post di revisione del codice: Split a string into chunks of the same length quindi ripeterò qui solo le parti rilevanti.

  • Stai partizionamento oltre Char ma String è UTF-16 codificato allora si può produrre rotti corde in almeno tre casi:

    1. Un carattere è codificato con più di un'unità di codice. Il codice Unicode per quel carattere è codificato come due unità di codice UTF-16, ciascuna unità di codice può finire in due sezioni diverse (e entrambe le stringhe saranno non valide).
    2. Un carattere è composto da più di un punto di codice. Hai a che fare con un personaggio composto da due punti di codice Unicode separati (ad esempio carattere Han).
    3. Un personaggio ha combinazioni di caratteri o modificatori. Questo è più comune di quanto si potrebbe pensare: per esempio Unicode che unisce personaggio come U + 0300 COMBINAZIONE DI GRAVE ACCENT utilizzato per costruire à e Unicode modificatori come U + 02BC di modifica: Apostrophe.
  • Definizione carattere per un linguaggio di programmazione e per un essere umano sono piuttosto diverse, ad esempio in Slovacco è un singolo carattere ma è fatta da 2/3 punti di codice Unicode che sono in questo caso anche 2/3 unità di codice UTF-16 quindi "dž".Length > 1. Ulteriori informazioni su questo e altri problemi culturali su How can I perform a Unicode aware character by character comparison?.
  • Esistono legature. Supponendo che una legatura sia un punto di codice (e anche supponendo che sia codificata come un'unità di codice), la tratteremo come un singolo glifo, tuttavia rappresenta due caratteri. Cosa fare in questo caso? Nella definizione generale di il carattere potrebbe essere piuttosto vago perché ha un diverso che significa in base alla disciplina in cui viene utilizzata questa parola. Non è possibile (probabilmente) gestire tutto correttamente ma è necessario impostare alcuni vincoli e il comportamento del codice del documento.

quello proposto (e non testato) implementazione può essere questo:

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

Non è ottimizzato per la velocità (come potete vedere ho cercato di mantenere il codice breve e chiaro con enumerazioni), ma, per i file di grandi dimensioni , funziona ancora meglio della tua implementazione (vedi il prossimo paragrafo per la ragione).

proposito la nota del codice che:

  • Si sta costruendo un enorme ArrayList di tenere risultato (?!). Si noti inoltre che in questo modo si ridimensiona più volte ArrayList (anche se, date le dimensioni di input e le dimensioni del blocco, è nota la sua dimensione finale).
  • strSegmentData viene ricostruito più volte, se è necessario accumulare caratteri è necessario utilizzare StringBuilder altrimenti ogni operazione alloca una nuova stringa e copia il vecchio valore (è lento e aggiunge anche pressione a Garbage Collector).

ci sono implementazioni più veloci (vedi collegata posta Code Review, soprattutto Heslacher's implementation per una versione molto più veloce) e se non è necessario per gestire correttamente Unicode (tu sei sicuro di gestire caratteri ASCII solo Stati Uniti), allora c'è anche un bel readable implementation from Jon Skeet (nota che, dopo aver profilato il tuo codice, puoi ancora migliorare le sue prestazioni per i file di grandi dimensioni che pre-allocano l'elenco di output della giusta dimensione). Non ripeto il loro codice qui, quindi fai riferimento ai post collegati.

Nella vostra specifica non c'è bisogno di leggere tutto il file in memoria enorme, è possibile leggere/analizzare n caratteri al momento (non preoccupatevi troppo di accesso al disco, I/O è tamponato). Ridurrà leggermente le prestazioni, ma migliorerà notevolmente l'utilizzo della memoria. In alternativa puoi leggere riga per riga (riuscendo a gestire i blocchi trasversali).

0

Qui di seguito è la mia analisi della tua domanda e il codice (leggere i commenti)

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

Rendere tutti i tipi di assunzione, sotto è il codice mi sento di raccomandare per la scissione lunga serie. È solo un modo pulito di fare ciò che stai facendo nell'esempio. Puoi ottimizzarlo, ma non sei sicuro di quanto velocemente stai cercando.

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

Non ho eseguito alcun test delle prestazioni sulla mia versione, ma suppongo che sia più veloce della versione.

Inoltre, non sono sicuro di come si prevede di consumare l'output, ma una buona ottimizzazione quando si esegue l'I/O è di utilizzare le chiamate asincrone.E una buona ottimizzazione (a costo di leggibilità e complessità) durante la manipolazione di grandi dimensioni string è quello di attaccare con char[]

Nota che

  • si potrebbe avere a che fare con problemi di codifica dei caratteri durante la lettura del file di
  • Se hai già la lunga stringa in memoria e la lettura dei file è stata inclusa nella demo, dovresti utilizzare la classe StringReader invece della classe StreamReader