web-dev-qa-db-fra.com

Quel est un moyen rapide de forcer CRLF en C #/.NET?

Comment normaliseriez-vous toutes les séquences de nouvelles lignes d'une chaîne en un type?

Je cherche à les rendre tous CRLF à des fins de courrier électronique (documents MIME). Dans l'idéal, il s'agirait d'une méthode statique, s'exécutant très rapidement et n'utilisant pas d'expressions régulières (car les variances des sauts de ligne, des retours chariot, etc. sont limitées). Peut-être qu'il y a même une méthode BCL que j'ai oubliée?

Hypothèse: Après y avoir réfléchi un peu plus, je pense que c'est une hypothèse sûre de dire que les CR sont soit autonomes soit font partie de la séquence CRLF. Autrement dit, si vous voyez CRLF, vous savez que tous les CR peuvent être supprimés. Sinon, il est difficile de dire combien de lignes doivent sortir de quelque chose comme "\ r\n\n\r".

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

Cela fonctionnera si l'entrée ne contient qu'un seul type de sauts de ligne: CR, LF ou CR + LF.

58

Cela dépend de exactement quelles sont les exigences. En particulier, comment voulez-vous gérer "\ r" seul? Cela devrait-il compter ou non comme un saut de ligne? Par exemple, comment "a\n\rb" doit-il être traité? Ce saut de ligne est-il très étrange, un "\ n", puis un "\ r" non autorisé, ou deux sauts de ligne distincts? Si "\ r" et "\ n" peuvent tous deux être des sauts de ligne, pourquoi "\ r\n" ne devrait-il pas être traité comme deux sauts de ligne?

Voici un code que je soupçonne être raisonnablement efficace.

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

Variante simple:

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

Pour une meilleure performance:

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

string normalized = nonNormalized.Replace("\r", "\n").Replace("\n", "\r\n");
4
Nathan

C'est un moyen rapide de le faire, je veux dire.

Il n'utilise pas une fonction regex coûteuse . Il n'utilise pas non plus plusieurs fonctions de remplacement que chacune a individuellement placées en boucle sur les données avec plusieurs vérifications, allocations, etc.

La recherche est donc effectuée directement dans 1 pour la boucle. Pour le nombre de fois où la capacité du tableau de résultats doit être augmentée, une boucle est également utilisée dans la fonction Array.Copy. Ce sont toutes les boucles ... Dans certains cas, une plus grande taille de page peut être plus efficace.

public static string NormalizeNewLine(this string val)
{
    if (string.IsNullOrEmpty(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);
}

Je pense maintenant que "\ n\r" n’est pas réellement utilisé sur les plates-formes de base. mais: qui utiliserait deux types de sauts de ligne en succession pour indiquer 2 sauts de ligne? Si vous voulez le savoir, vous devez jeter un coup d’œil avant de savoir si les\n et\r sont tous deux utilisés séparément. document.

0
Roberto B