2009-05-08 5 views
36

Come normalizzereste tutte le sequenze di nuova riga di una stringa in un tipo?Qual è un modo rapido per forzare CRLF in C#/.NET?

Sto cercando di renderli tutti CRLF ai fini della posta elettronica (documenti MIME). Idealmente questo sarebbe avvolto in un metodo statico, eseguito molto rapidamente, e non usando espressioni regolari (poiché le variazioni di interruzioni di riga, ritorni a capo, ecc. Sono limitate). Forse c'è anche un metodo BCL che ho trascurato?

ASSUNZIONE: Dopo aver dato un po 'più di riflessione, penso che sia un presupposto sicuro dire che i CR sono o stand-alone o parte della sequenza CRLF. Cioè, se vedi CRLF allora sai che tutti i CR possono essere rimossi. Altrimenti è difficile dire quante linee dovrebbero uscire da qualcosa come "\ r \ n \ n \ r".

risposta

52
input.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n") 

Ciò funzionerà se l'input contiene un solo tipo di interruzioni di linea - o CR o LF o CR + LF.

+0

Funziona anche per visualizzare testo sconosciuto su una pagina HTML utilizzando l'ultima sostituzione per inserire un tag BR. Server.HtmlEncode (input) .Replace ("\ r \ n", "\ n"). Sostituisci ("\ r", "\ n"). Sostituisci ("\ n", "
"); –

+0

Questa giunzione risolve i problemi con i modelli T4.Continuavo a ottenere dei ritorni folli nel mio output generato. – DaImTo

4
string nonNormalized = "\r\n\n\r"; 

string normalized = nonNormalized.Replace("\r", "\n").Replace("\n", "\r\n"); 
+1

Questo esempio produce quattro interruzioni di riga, mentre la stringa non normalizzata contiene due. –

+0

È vero, fa sorgere una buona domanda su quando viene usata una sequenza e quando viene semplicemente rimossa (ignorata). –

28

Dipende dallo esattamente quali sono i requisiti. In particolare, come vuoi gestire "\ r" da solo? Questo dovrebbe essere considerato come una interruzione di linea o no? Ad esempio, come dovrebbe essere trattato "a \ n \ rb"? Questa è un'interruzione di riga molto strana, una "\ n" interruzione e poi una canaglia "\ r", o due interruzioni di riga separate? Se "\ r" e "\ n" possono essere entrambi interruzioni di riga, perché "\ r \ n" non deve essere considerato come due interruzioni di riga?

Ecco un codice che sospetto sia ragionevolmente efficiente.

using System; 
using System.Text; 

class LineBreaks 
{  
    static void Main() 
    { 
     Test("a\nb"); 
     Test("a\nb\r\nc"); 
     Test("a\r\nb\r\nc"); 
     Test("a\rb\nc"); 
     Test("a\r"); 
     Test("a\n"); 
     Test("a\r\n"); 
    } 

    static void Test(string input) 
    { 
     string normalized = NormalizeLineBreaks(input); 
     string debug = normalized.Replace("\r", "\\r") 
           .Replace("\n", "\\n"); 
     Console.WriteLine(debug); 
    } 

    static string NormalizeLineBreaks(string input) 
    { 
     // Allow 10% as a rough guess of how much the string may grow. 
     // If we're wrong we'll either waste space or have extra copies - 
     // it will still work 
     StringBuilder builder = new StringBuilder((int) (input.Length * 1.1)); 

     bool lastWasCR = false; 

     foreach (char c in input) 
     { 
      if (lastWasCR) 
      { 
       lastWasCR = false; 
       if (c == '\n') 
       { 
        continue; // Already written \r\n 
       } 
      } 
      switch (c) 
      { 
       case '\r': 
        builder.Append("\r\n"); 
        lastWasCR = true; 
        break; 
       case '\n': 
        builder.Append("\r\n"); 
        break; 
       default: 
        builder.Append(c); 
        break; 
      } 
     } 
     return builder.ToString(); 
    } 
} 
+0

Molto bello; questo sarebbe sicuramente utile per un input più arbitrario! Per il mio caso ho scelto di andare con una ipotesi (fatta una modifica), ma ho votato a prescindere. –

+0

Giusto. Se le prestazioni sono davvero significative, potresti voler confrontare questa soluzione con quella accettata, ma solo se hai effettivamente accertato che è significativa tramite un profiler! Vorrei * sperare * che questo sia più veloce, in quanto richiede solo un singolo passaggio attraverso i dati. –

+0

Cosa si intende per usare RegExpr? non buone prestazioni? http://stackoverflow.com/questions/140926/normalize-newlines-in-c-sharp – Kiquenet

3

variante Semplice:

Regex.Replace(input, @"\r\n|\r|\n", "\r\n") 

Per migliorare le prestazioni:

static Regex newline_pattern = new Regex(@"\r\n|\r|\n", RegexOptions.Compiled); 
[...] 
    newline_pattern.Replace(input, "\r\n"); 
0

Questo è un modo rapido per farlo, voglio dire.

Non utilizza una funzione regex costosa. Inoltre non utilizza più funzioni di sostituzione che singolarmente hanno eseguito il loop sui dati con diversi controlli, allocazioni e così via.

Quindi la ricerca viene eseguita direttamente in ciclo 1 for. Per il numero di volte che la capacità dell'array dei risultati deve essere aumentata, viene utilizzato anche un loop all'interno della funzione Array.Copy. Questi sono tutti i loop. In alcuni casi, una dimensione di pagina più grande potrebbe essere più efficiente.

public static string NormalizeNewLine(this string val) { 
    if (string.IsNullOrWhiteSpace(val)) 
     return val; 

    const int page = 6; 
    int a = page; 
    int j = 0; 
    int len = val.Length; 
    char[] res = new char[len]; 
    for (int i = 0; i < len; i++) { 
     char ch = val[i]; 
     if (ch == '\r') { 
      int ni = i + 1; 
      if (ni < len && val[ni] == '\n') { 
       res[j++] = '\r'; 
       res[j++] = '\n'; 
       i++; 
      } else { 
       if (a == page) { //ensure capacity 
        char[] nres = new char[res.Length + page]; 
        Array.Copy(res, 0, nres, 0, res.Length); 
        res = nres; 
        a = 0; 
       } 
       res[j++] = '\r'; 
       res[j++] = '\n'; 
       a++; 
      } 
     } 
     else if (ch == '\n') { 
      int ni = i + 1; 
      if (ni < len && val[ni] == '\r') { 
       res[j++] = '\r'; 
       res[j++] = '\n'; 
       i++; 
      } else { 
       if (a == page) { //ensure capacity 
        char[] nres = new char[res.Length + page]; 
        Array.Copy(res, 0, nres, 0, res.Length); 
        res = nres; 
        a = 0; 
       } 
       res[j++] = '\r'; 
       res[j++] = '\n'; 
       a++; 
      } 
     } else { 
      res[j++] = ch; 
     } 
    } 
    return new string(res, 0, j); 
} 

Ora che "\ n \ r" non è effettivamente utilizzato su piattaforme di base. ma: chi userebbe due tipi di interruzioni di riga in successione per indicare 2 interruzioni di riga? Se vuoi saperlo, devi dare un'occhiata prima di sapere se i \ n e \ r sono entrambi usati separatamente nello stesso documento.