Какой быстрый способ заставить CRLF в C # / .NET? - PullRequest
43 голосов
/ 08 мая 2009

Как бы вы нормализовали все последовательности новой строки в строке для одного типа?

Я хочу сделать их все CRLF для электронной почты (документы MIME). В идеале это должно быть заключено в статический метод, выполняемый очень быстро и без использования регулярных выражений (поскольку различия между переносами строк, возвратами каретки и т. Д. Ограничены). Возможно, есть даже метод BCL, который я пропустил?

ПРЕДПОЛОЖЕНИЕ: Подумав немного об этом, я думаю, что можно с уверенностью сказать, что CR являются либо автономными, либо частью последовательности CRLF. То есть, если вы видите CRLF, то вы знаете, что все CR могут быть удалены. В противном случае трудно сказать, сколько строк должно получиться из чего-то вроде "\ r \ n \ n \ r".

Ответы [ 5 ]

61 голосов
/ 08 мая 2009
input.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n")

Это будет работать, если вход содержит только один тип разрывов строк - либо CR, либо LF, либо CR + LF.

29 голосов
/ 08 мая 2009

Это зависит от точно , каковы требования. В частности, как вы хотите обрабатывать "\ r" самостоятельно? Это должно считаться разрывом строки или нет? Например, как следует относиться к «a \ n \ rb»? Это один очень странный разрыв строки, один разрыв "\ n" и затем разбойник "\ r", или два отдельных перехода на новую строку? Если "\ r" и "\ n" могут быть как самостоятельные разрывы строк, почему "\ r \ n" не следует рассматривать как два переноса строк?

Вот некоторый код, который я подозреваю, является разумно эффективным.

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();
    }
}
6 голосов
/ 20 мая 2015

Простой вариант:

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

Для лучшей производительности:

static Regex newline_pattern = new Regex(@"\r\n|\r|\n", RegexOptions.Compiled);
[...]
    newline_pattern.Replace(input, "\r\n");
4 голосов
/ 08 мая 2009
string nonNormalized = "\r\n\n\r";

string normalized = nonNormalized.Replace("\r", "\n").Replace("\n", "\r\n");
0 голосов
/ 17 ноября 2017

Это быстрый способ сделать это, я имею в виду.

Он не использует дорогую функцию регулярных выражений. Он также не использует несколько функций замещения, каждая из которых в отдельности выполняла цикл по данным с несколькими проверками, распределениями и т. Д.

Таким образом, поиск выполняется непосредственно в одном цикле for. Для того, сколько раз необходимо увеличить емкость массива результатов, в функции Array.Copy также используется цикл. Это все петли. В некоторых случаях более эффективный размер страницы может быть более эффективным.

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

Теперь я понимаю, что \ n \ r на самом деле не используется на базовых платформах. Но кто будет использовать два типа разрывов строк подряд для обозначения двух разрывов строк?

Если вы хотите это знать, то вам нужно посмотреть, прежде чем узнать, используются ли оба \ n и \ r по отдельности в одном документе.

...