文字列内のすべての改行シーケンスを1つのタイプにどのように正規化しますか?
電子メール(MIMEドキュメント)を目的として、すべてをCRLFにすることを検討しています。理想的には、これは静的メソッドにラップされ、非常に高速に実行され、正規表現を使用しません(改行、キャリッジリターンなどの分散が制限されるため)。おそらく私が見落としたBCLメソッドもありますか?
仮定:これについてもう少し考えた後、CRはスタンドアロンまたはCRLFシーケンスの一部であると言うのは安全な仮定だと思います。つまり、CRLFが表示される場合、すべてのCRを削除できることがわかります。そうでなければ、「\ r\n\n\r」のようなものから何行出てくるかを伝えるのは困難です。
input.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n")
これは、入力に1種類の改行(CR、LF、またはCR + LF)のみが含まれている場合に機能します。
正確に要件に依存します。特に、「\ r」を単独でどのように処理しますか?それは改行としてカウントされるべきですか?例として、「a\n\rb」はどのように扱われるべきですか?それは非常に奇妙な改行、1つの「\ n」改行、次に不正な「\ r」、または2つの別々の改行ですか? 「\ r」と「\ n」の両方を単独で改行できる場合、「\ r\n」を2つの改行として扱わないのはなぜですか?
合理的に効率的と思われるコードをいくつか示します。
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();
}
}
単純なバリアント:
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");
string nonNormalized = "\r\n\n\r";
string normalized = nonNormalized.Replace("\r", "\n").Replace("\n", "\r\n");
これは簡単な方法です、つまり。
高価な正規表現関数を使用しません。また、それぞれが複数のチェック、割り当てなどでデータをループする複数の置換関数も使用しません。
したがって、検索は1つの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'は実際には基本的なプラットフォームでは使用されないようになりました。しかし、2つのタイプの改行を連続して使用して、2つのタイプの改行を示すのは誰でしょうか?
そのことを知りたい場合は、同じドキュメントで\ nと\ rの両方が別々に使用されているかどうかを確認する必要があります。