Эффективное удаление всех пробельных строк из многострочной строки
В С# какой лучший способ удалить пустые строки, т.е. строки, содержащие только пробелы из строки? Я рад использовать Regex, если это лучшее решение.
EDIT: я должен добавить, что я использую .NET 2.0.
Обновление Bounty: я отброшу это обратно после награды, но я хотел прояснить несколько вещей.
Сначала будет работать любое регулярное выражение Perl 5 compat. Это не ограничивается разработчиками .NET. Заголовок и теги были отредактированы, чтобы отразить это.
Во-вторых, хотя я привел краткий пример в деталях подробностей, это не единственный тест, который вы должны удовлетворить. Ваше решение должно удалить все строки, которые состоят из ничего, кроме пробелов, , а также последней новой строки. Если есть строка, которая после запуска вашего регулярного выражения заканчивается символом "/r/n" или любым пробельным символом, он не работает.
Ответы
Ответ 1
Если вы хотите удалить строки, содержащие пробелы (вкладки, пробелы), попробуйте:
string fix = Regex.Replace(original, @"^\s*$\n", string.Empty, RegexOptions.Multiline);
Изменить (для @Will): самым простым решением для обрезки последних строк будет использование TrimEnd
в результирующей строке, например:
string fix =
Regex.Replace(original, @"^\s*$\n", string.Empty, RegexOptions.Multiline)
.TrimEnd();
Ответ 2
string outputString;
using (StringReader reader = new StringReader(originalString)
using (StringWriter writer = new StringWriter())
{
string line;
while((line = reader.ReadLine()) != null)
{
if (line.Trim().Length > 0)
writer.WriteLine(line);
}
outputString = writer.ToString();
}
Ответ 3
с моей головы...
string fixed = Regex.Replace(input, "\s*(\n)","$1");
делает это:
fdasdf
asdf
[tabs]
[spaces]
asdf
в это:
fdasdf
asdf
asdf
Ответ 4
Использование LINQ:
var result = string.Join("\r\n",
multilineString.Split(new string[] { "\r\n" }, ...None)
.Where(s => !string.IsNullOrWhitespace(s)));
Если вы имеете дело с большими вводами и/или несогласованными окончаниями строк, вы должны использовать StringReader и выполнять предыдущую старую школу с циклом foreach.
Ответ 5
Хорошо, что этот ответ соответствует уточненным требованиям, указанным в награде:
Мне также нужно удалить любые завершающие символы новой строки, а мое Regex-fu терпит неудачу. Моя щедрость идет к любому, кто может дать мне регулярное выражение, которое проходит этот тест: StripWhitespace ( "test\r\n\r\nthis\r\n\r\n" ) == "тест\г\nЭто"
Итак, ответ:
(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|(\r?\n)+\z
Или в коде С#, предоставленном @Chris Schmich:
string fix = Regex.Replace("test\r\n \r\nthis\r\n\r\n", @"(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|(\r?\n)+\z", string.Empty, RegexOptions.Multiline);
Теперь попробуем это понять. Здесь есть три необязательных шаблона, которые я готов заменить на string.empty
.
-
(?<=\r?\n)(\s*$\r?\n)+
- соответствует одному неограниченному числу, содержащему только пробел и предшествующий разрыву строки (но не соответствует первым перерывам строки).
-
(?<=\r?\n)(\r?\n)+
- соответствует одному из неограниченных пустых строк без содержимого, которое предшествует разрыву строки (но не соответствует первым прерываниям строки).
-
(\r?\n)+\z
- соответствует одному для неограниченных разрывов строк в конце тестируемой строки (трейлинг-строки прерываются по мере их вызова)
Это отлично удовлетворяет вашему тесту! Но также удовлетворяет стилям стирания стилей \r\n
и \n
! Проверьте это! Я считаю, что это будет самый правильный ответ, хотя более простое выражение передаст ваш заданный критерий бонуса, это регулярное выражение проходит более сложные условия.
РЕДАКТИРОВАТЬ: @Указывает на потенциальную ошибку в последнем совпадении шаблонов указанного выше регулярного выражения, поскольку он не будет соответствовать разрыву строк, содержащему пробел в конце тестовой строки. Поэтому давайте изменим этот последний шаблон на это:
\b\s+\z
\b является границей слов (начало или конец слова),\s + является одним или несколькими символами пробела, \z - это конец тестовой строки (конец "файла" ), Таким образом, теперь он будет соответствовать любому ассортименту пробелов в конце файла, включая вкладки и пробелы, а также возврат каретки и разрывы строк. Я тестировал как @Will предоставил тестовые примеры.
Итак, теперь все вместе, это должно быть:
(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|\b\s+\z
РЕДАКТИРОВАТЬ № 2: Хорошо, есть еще один возможный случай. @Wil обнаружил, что последнее регулярное выражение не распространяется. Этот случай представляет собой входные данные, которые имеют разрывы строк в начале файла перед любым контентом. Поэтому давайте добавим еще один шаблон в соответствие с началом файла.
\A\s+
- \A
соответствует началу файла, \s+
соответствует одному или нескольким символам пробела.
Итак, теперь у нас есть:
\A\s+|(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|\b\s+\z
Итак, теперь у нас есть четыре шаблона для сопоставления:
- пробел в начале файла,
- избыточные разрывы строк, содержащие пробелы, (например:
\r\n \r\n\t\r\n
)
- избыточные разрывы строк без содержимого, (например:
\r\n\r\n
)
- пробел в конце файла
Ответ 6
В ответ на награду Will, которая ожидает решение, которое принимает "test\r\n \r\nthis\r\n\r\n"
и выводит "test\r\nthis"
, я придумал решение, которое использует атомная группировка (aka Nonbacktracking Subexpressions в MSDN). Я рекомендую прочитать эти статьи, чтобы лучше понять, что происходит. В конечном итоге атомная группа помогла совместить конечные символы новой строки, которые в остальном остались позади.
Используйте RegexOptions.Multiline
с этим шаблоном:
^\s+(?!\B)|\s*(?>[\r\n]+)$
Вот пример с некоторыми тестовыми примерами, включая некоторые, которые я собрал из комментариев Уилла на другие сообщения, а также мои собственные.
string[] inputs =
{
"one\r\n \r\ntwo\r\n\t\r\n \r\n",
"test\r\n \r\nthis\r\n\r\n",
"\r\n\r\ntest!",
"\r\ntest\r\n ! test",
"\r\ntest \r\n ! "
};
string[] outputs =
{
"one\r\ntwo",
"test\r\nthis",
"test!",
"test\r\n ! test",
"test \r\n ! "
};
string pattern = @"^\s+(?!\B)|\s*(?>[\r\n]+)$";
for (int i = 0; i < inputs.Length; i++)
{
string result = Regex.Replace(inputs[i], pattern, "",
RegexOptions.Multiline);
Console.WriteLine(result == outputs[i]);
}
РЕДАКТИРОВАТЬ: Чтобы решить проблему с шаблоном, который не смог очистить текст с помощью сочетания пробелов и новых строк, я добавил \s*
в последнюю часть чередования регулярного выражения. Моя предыдущая модель была избыточной, и я понял, что \s*
будет обрабатывать оба случая.
Ответ 7
Нехорошо. Я бы использовал этот, используя JSON.net:
var o = JsonConvert.DeserializeObject(prettyJson);
new minifiedJson = JsonConvert.SerializeObject(o, Formatting.None);
Ответ 8
string corrected =
System.Text.RegularExpressions.Regex.Replace(input, @"\n+", "\n");
Ответ 9
Здесь другой вариант: используйте класс StringReader
. Преимущества: один проход по строке, не создает промежуточных массивов.
public static string RemoveEmptyLines(this string text) {
var builder = new StringBuilder();
using (var reader = new StringReader(text)) {
while (reader.Peek() != -1) {
string line = reader.ReadLine();
if (!string.IsNullOrWhiteSpace(line))
builder.AppendLine(line);
}
}
return builder.ToString();
}
Примечание: метод IsNullOrWhiteSpace
новый в .NET 4.0. Если у вас этого нет, тривиально писать самостоятельно:
public static bool IsNullOrWhiteSpace(string text) {
return string.IsNullOrEmpty(text) || text.Trim().Length < 1;
}
Ответ 10
Пойду с:
public static string RemoveEmptyLines(string value) {
using (StringReader reader = new StringReader(yourstring)) {
StringBuilder builder = new StringBuilder();
string line;
while ((line = reader.ReadLine()) != null) {
if (line.Trim().Length > 0)
builder.AppendLine(line);
}
return builder.ToString();
}
}
Ответ 11
В ответ на то, что Will Bounty - это подпрограмма Perl, которая дает правильный ответ на тестовый пример:
sub StripWhitespace {
my $str = shift;
print "'",$str,"'\n";
$str =~ s/(?:\R+\s+(\R)+)|(?:()\R+)$/$1/g;
print "'",$str,"'\n";
return $str;
}
StripWhitespace("test\r\n \r\nthis\r\n\r\n");
выход:
'test
this
'
'test
this'
Чтобы не использовать \R
, замените его на [\r\n]
и инвертируйте альтернативу. Это дает тот же результат:
$str =~ s/(?:(\S)[\r\n]+)|(?:[\r\n]+\s+([\r\n])+)/$1/g;
Нет необходимости в специальной настройке без поддержки нескольких линий. Тем не менее вы можете добавить флаг s
, если это необходимо.
$str =~ s/(?:(\S)[\r\n]+)|(?:[\r\n]+\s+([\r\n])+)/$1/sg;
Ответ 12
если его единственные белые пространства, почему вы не используете метод строки С#
string yourstring = "A O P V 1.5";
yourstring.Replace(" ", string.empty);
Результатом будет "AOPV1.5"
Ответ 13
char[] delimiters = new char[] { '\r', '\n' };
string[] lines = value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
string result = string.Join(Environment.NewLine, lines)
Ответ 14
Вот что-то простое, если работать против каждой отдельной строки...
(^\s+|\s+|^)$
Ответ 15
Э. Ну, после всего этого, я не мог найти тот, который ударил бы по всем углам, которые я мог бы выяснить. Следующее - мое последнее заклинание регулярного выражения, которое разбивает
- Все пустые строки с начала строки
- Не включая пробелы в начале первой строки без пробелов
- Все пустые строки после первой строки без пробелов и до последней строки без пробелов
- Опять же, сохранение всех пробелов в начале любой строки без пробелов
- Все пустые строки после последней строки без пробелов, включая последнюю строку новой строки
(? & Л; = (\ г\п) | ^)\S *\г\п |\г\N\S * $
который по существу говорит:
- Сразу после
- Начало строки OR
- Конец последней строки
- Сопоставьте как можно больше смежных пробелов, которые заканчиваются символом новой строки *
- ИЛИ
- Соответствует новой строке и максимально возможному смежному пробелу, который заканчивается в конце строки
Первая половина ловит все пробелы в начале строки до первой строки без пробелов или всех пробелов между непробежными линиями. Вторая половина помешает оставшемуся пробелу в строке, включая последнюю строку строки без строки пробела.
Спасибо всем, кто пытался помочь; ваши ответы помогли мне продумать все, что мне нужно было учитывать при сопоставлении.
* (Это регулярное выражение считает, что новая строка будет \r\n
, поэтому ее нужно будет скорректировать в зависимости от источника строки. Для запуска соответствия не нужно устанавливать параметры.)
Ответ 16
Расширение строки
public static string UnPrettyJson(this string s)
{
try
{
// var jsonObj = Json.Decode(s);
// var sObject = Json.Encode(value); dont work well with array of strings c:['a','b','c']
object jsonObj = JsonConvert.DeserializeObject(s);
return JsonConvert.SerializeObject(jsonObj, Formatting.None);
}
catch (Exception e)
{
throw new Exception(
s + " Is Not a valid JSON ! (please validate it in http://www.jsoneditoronline.org )", e);
}
}
Ответ 17
Попробуйте это.
string s = "Test1" + Environment.NewLine + Environment.NewLine + "Test 2";
Console.WriteLine(s);
string result = s.Replace(Environment.NewLine, String.Empty);
Console.WriteLine(result);
Ответ 18
s = Regex.Replace(s, @"^[^\n\S]*\n", "");
[^\n\S]
соответствует любому символу, который не является символом строки или символом без пробела, поэтому любой символ пробела, кроме \n
. Но, скорее всего, единственными персонажами, о которых вам нужно беспокоиться, являются космос, вкладка и возврат каретки, поэтому это тоже должно работать:
s = Regex.Replace(s, @"^[ \t\r]*\n", "");
И если вы хотите, чтобы он поймал последнюю строку, без окончательного перевода строки:
s = Regex.Replace(s, @"^[ \t\r]*\n?", "");