Подтвердить что ты не робот

Эффективное удаление всех пробельных строк из многострочной строки

В С# какой лучший способ удалить пустые строки, т.е. строки, содержащие только пробелы из строки? Я рад использовать Regex, если это лучшее решение.

EDIT: я должен добавить, что я использую .NET 2.0.


Обновление Bounty: я отброшу это обратно после награды, но я хотел прояснить несколько вещей.

Сначала будет работать любое регулярное выражение Perl 5 compat. Это не ограничивается разработчиками .NET. Заголовок и теги были отредактированы, чтобы отразить это.

Во-вторых, хотя я привел краткий пример в деталях подробностей, это не единственный тест, который вы должны удовлетворить. Ваше решение должно удалить все строки, которые состоят из ничего, кроме пробелов, , а также последней новой строки. Если есть строка, которая после запуска вашего регулярного выражения заканчивается символом "/r/n" или любым пробельным символом, он не работает.

4b9b3361

Ответ 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?", "");