Если вы хотите полностью разобраться с переносимостью (и да, я только добавляю этот ответ в ответ на упоминание Аланом старого Mac-стиля \ r), тогда вы хотите охватить:
* nixСтиль: \n
Стиль DOS / Windows: \r\n
Старый стиль Mac: \r
Стиль EBCDIC: \u0085
(возможно, немного более актуальный -я бы предпочел использовать его раньше, чем старый Mac).
Символ форматирования разделителя строк: \u2028
Символ форматирования разделителя абзацев: \u2029
Давайте простоне останавливаться на точной семантике \u000B
и \u000C
и превратить это в нечто разумное (в конце концов).Если бы мы были , чтобы попытаться разобраться со всем этим.Как бы мы это сделали?
С 6 различными переносами строк, один из которых является комбинацией двух других, но которые не должны рассматриваться как два переноса строк, рассматривая это в регистре.Сам ex может быть неприятным.
Гораздо лучше было бы отфильтровать их все в обертке TextReader:
public class LineBreakNormaliser : TextReader
{
private readonly TextReader _source;
private bool isNewLine(int charAsInt)
{
switch(charAsInt)
{
case '\n': case '\r':
case '\u0085': case '\u2028': case '\u2029':
case '\u000B': case '\u000C':
return true;
default:
return false;
}
}
public LineBreakNormaliser(TextReader source)
{
_source = source;
}
public override void Close()
{
_source.Close();
base.Close();
}
protected override void Dispose(bool disposing)
{
if(disposing)
_source.Dispose();
base.Dispose(disposing);
}
public override int Peek()
{
int i = _source.Peek();
if(i == -1)
return -1;
if(isNewLine(i))
return '\n';
return i;
}
public override int Read()
{
int i = _source.Read();
if(i == -1)
return -1;
if(i == '\r')
{
if(_source.Peek() == '\n')
_source.Read(); //eat next half of CRLF pair.
return i;
}
if(isNewLine(i))
return '\n';
return i;
}
public override int Read(char[] buffer, int index, int count)
{
//We take advantage of the fact that we are allowed to return fewer than requested.
//ReadBlock does the work for us for those who need the full amount:
char[] tmpBuffer = new char[count];
int cChars = count = _source.Read(tmpBuffer, 0, count);
if(cChars == 0)
return 0;
for(int i = 0; i != cChars; ++i)
{
char cur = tmpBuffer[i];
if(cur == '\r')
{
if(i == cChars -1)
{
if(_source.Peek() == '\n')
{
_source.Read(); //eat second half of CRLF
--count;
}
}
else if(tmpBuffer[i + 1] == '\r')
{
++i;
--count;
}
buffer[index++] = '\n';
}
else if(isNewLine(cur))
buffer[index++] = '\n';
else
buffer[index++] = '\n';
}
return count;
}
}
Если вы читаете файл с помощью этого текстового ридера, то с этого моментаВаше регулярное выражение может зависеть от единственной новой строки, являющейся \n
, как и любой другой код.
Это сделано, регулярное выражение может быть на самом деле проще, чем когда-либо, и вы, хотя и полностью излишне для этого единственного случая (и только написанопотому что после упоминания Алана об OS9 и более ранней идее поддержки машин IBM EBCDIC меня позабавила), она может использоваться повторно для всех других случаев, в которых контекст на самом деле не является чрезмерным уничтожением, потому что она становится «просто использовать проверенную линиюНормализатор, чтобы сделать вещи проще ".(После того, как это хорошо проверено, я не проверял ничего из вышеперечисленного).