wake-up-neo.net

Alle Leerzeichenzeilen aus einer mehrzeiligen Zeichenfolge effizient entfernen

Was ist der beste Weg, leere Zeilen in C # zu entfernen, d. H. Zeilen, die nur Leerzeichen aus einer Zeichenfolge enthalten? Ich bin froh, eine Regex zu verwenden, wenn dies die beste Lösung ist.

BEARBEITEN: Ich sollte hinzufügen, dass ich .NET 2.0 verwende.


Kopfgeld-Update: Ich werde das zurückrollen, nachdem das Kopfgeld vergeben wurde, aber ich wollte ein paar Dinge klären.

Zunächst funktioniert jeder Perl 5-Compat-Regex. Dies ist nicht auf .NET-Entwickler beschränkt. Der Titel und die Tags wurden entsprechend bearbeitet.

Zweitens, obwohl ich in den Kopfgelddetails ein schnelles Beispiel gegeben habe, ist es nicht der only -Test, den Sie erfüllen müssen. Ihre Lösung muss entfernen alle Zeilen, die nur aus Leerzeichen bestehen, sowie der letzte Zeilenvorschub . Wenn eine Zeichenfolge vorhanden ist, die nach dem Durchlaufen der Regex mit "/ r/n" oder einem beliebigen Leerzeichen endet, schlägt die Zeichenfolge fehl. 

30
FunLovinCoder

Wenn Sie Zeilen entfernen möchten, die Leerzeichen enthalten (Tabulatoren, Leerzeichen), versuchen Sie Folgendes:

string fix = Regex.Replace(original, @"^\s*$\n", string.Empty, RegexOptions.Multiline);

Bearbeiten (für @Will): Die einfachste Lösung zum Trimmen nachfolgender Zeilenumbrüche wäre die Verwendung von TrimEnd für den resultierenden String, z.

string fix =
    Regex.Replace(original, @"^\s*$\n", string.Empty, RegexOptions.Multiline)
         .TrimEnd();
20
Chris Schmich
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();
}
16
Thomas Levesque

aus meinem Kopf ...

string fixed = Regex.Replace(input, "\s*(\n)","$1");

dreht das:

 fdasdf 
 asdf 
 [Registerkarten] 

 [Leerzeichen] 

 asdf 


das sehr gut finden:

 fdasdf 
 asdf 
 asdf 
13
Sky Sanders

Mit LINQ:

var result = string.Join("\r\n",
                 multilineString.Split(new string[] { "\r\n" }, ...None)
                                .Where(s => !string.IsNullOrWhitespace(s)));

Wenn Sie mit großen Eingaben und/oder inkonsistenten Zeilenenden arbeiten, sollten Sie einen StringReader verwenden und stattdessen die oben genannte Old School mit einer foreach-Schleife ausführen.

8
dtb

nicht gut. Ich würde dieses mit JSON.net verwenden:

var o = JsonConvert.DeserializeObject(prettyJson);
new minifiedJson = JsonConvert.SerializeObject(o, Formatting.None);
3
Yuki

Okay, diese Antwort entspricht den in der Prämie angegebenen klargestellten Anforderungen:

Ich muss auch nachstehende Zeilenumbrüche entfernen und mein Regex-fu lautet Versagen. Mein Kopfgeld geht an jeden, der mir einen Regex geben kann, der bestanden hat Diesen Test: StripWhitespace ("test\r\n\r\n\r\n\r\n\r\n\r\n") == "test\r\ndieses"

Also hier ist die Antwort:

(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|(\r?\n)+\z

Oder im C # -Code von @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);

Versuchen wir es jetzt zu verstehen. Es gibt drei optionale Muster, die ich durch string.empty ersetzen möchte.

  1. (?<=\r?\n)(\s*$\r?\n)+ - ordnet eine bis unbegrenzte Anzahl von Zeilen zu, die nur Leerzeichen enthalten und vor einem Zeilenumbruch stehen (entspricht jedoch nicht den ersten vorangehenden Zeilenumbrüchen).
  2. (?<=\r?\n)(\r?\n)+ - ordnet eine bis unbegrenzt leere Zeile mit keinem Inhalt zu, dem ein Zeilenumbruch vorangeht (entspricht jedoch nicht den ersten vorhergehenden Zeilenumbrüchen).
  3. (\r?\n)+\z - Stellt einen bis unbegrenzten Zeilenumbruch am Ende des getesteten Strings dar (Zeilenumbrüche, wie Sie sie aufgerufen haben)

Damit ist dein Test perfekt erfüllt! Erfüllt aber auch die Zeilenumbrüche von \r\n und \n! Testen Sie es! Ich glaube, dass dies die zutreffendste Antwort sein wird, obwohl ein einfacher Ausdruck den angegebenen Kopfgeldtest bestehen würde, diese Regex die komplexeren Bedingungen.

EDIT: @Will hat auf einen potenziellen Fehler im letzten Mustervergleich des obigen Regex hingewiesen, da er nicht mit mehreren Zeilenumbrüchen übereinstimmt, die Leerzeichen am Ende der Testzeichenfolge enthalten. Also, lasst uns das letzte Muster ändern: 

\b\s+\z Das\b ist eine Word-Grenze (Anfang oder ENDE eines Words), das\s + ist ein oder mehrere Leerzeichen, das\z ist das Ende der Testzeichenfolge (Ende von "Datei"). Daher wird jetzt jedes beliebige Whitespace am Ende der Datei angepasst, einschließlich Tabulatoren und Leerzeichen sowie Wagenrücklauf und Zeilenumbrüche. Ich habe beide von @ Wills bereitgestellten Testfälle getestet. 

Also alles zusammen jetzt sollte es sein:

(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|\b\s+\z

EDIT # 2: Okay, es gibt einen weiteren möglichen Fall @Wil hat festgestellt, dass der letzte Regex nicht abdeckt. Bei diesem Fall handelt es sich um Eingaben, bei denen vor dem Inhalt Zeilenumbrüche am Anfang der Datei stehen. Fügen wir also ein weiteres Muster hinzu, das dem Anfang der Datei entspricht.

\A\s+ - Der \A stimmt mit dem Anfang der Datei überein, der \s+ entspricht einem oder mehreren Leerzeichen.

Jetzt haben wir:

\A\s+|(?<=\r?\n)(\s*$\r?\n)+|(?<=\r?\n)(\r?\n)+|\b\s+\z

So haben wir jetzt vier Muster zum Abgleichen:

  1. leerzeichen am Anfang der Datei,
  2. redundante Zeilenumbrüche mit Leerzeichen (zB: \r\n \r\n\t\r\n)
  3. redundante Zeilenumbrüche ohne Inhalt (zB: \r\n\r\n)
  4. leerzeichen am Ende der Datei
3
BenSwayne

Als Antwort auf Wills Kopfgeld, das eine Lösung erwartet, die "test\r\n \r\nthis\r\n\r\n" und "test\r\nthis" ausgibt, habe ich eine Lösung gefunden, die atomare Gruppierung (aka Nonbacktracking Subexpressions auf MSDN) verwendet. Ich empfehle, diese Artikel zu lesen, um besser zu verstehen, was passiert. Letztendlich half die Atomic-Gruppe dabei, die ansonsten zurückgebliebenen nachgestellten Zeilenumbrüche abzugleichen.

Verwenden Sie RegexOptions.Multiline mit diesem Muster:

^\s+(?!\B)|\s*(?>[\r\n]+)$

Hier ist ein Beispiel mit einigen Testfällen, einschließlich einiger, die ich aus Wills Kommentaren zu anderen Posts sowie aus meinen eigenen Beiträgen erhalten habe.

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

BEARBEITEN: Um dem Problem zu begegnen, dass das Muster Text nicht mit einer Mischung aus Leerzeichen und Zeilenumbrüchen bereinigt, fügte ich \s* zum letzten Abschnitt der Änderung der Regex hinzu. Mein vorheriges Muster war redundant und ich erkannte, dass \s* beide Fälle handhaben würde.

2
Ahmad Mageed

Hier ist eine weitere Option: Verwenden Sie die StringReader-Klasse. Vorteile: Ein Durchlauf über die Zeichenfolge erzeugt keine Zwischenarrays.

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

Hinweis: Die IsNullOrWhiteSpace-Methode ist neu in .NET 4.0 . Wenn Sie das nicht haben, ist es trivial, alleine zu schreiben:

public static bool IsNullOrWhiteSpace(string text) {
    return string.IsNullOrEmpty(text) || text.Trim().Length < 1;
}
1
Dan Tao

Ich gehe mit:

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

wenn dies nur Leerzeichen sind, verwenden Sie die C # -Schnurmethode 

    string yourstring = "A O P V 1.5";
    yourstring.Replace("  ", string.empty);

ergebnis ist "AOPV1.5"

1
dnxit

Als Antwort auf Wills Kopfgeld gibt es hier ein Perl-Sub, das eine korrekte Antwort auf den Testfall gibt:

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

Ausgabe:

'test

this

'
'test
this'

Um \R nicht zu verwenden, ersetzen Sie es durch [\r\n] und kehren Sie die Alternative um. Dieser erzeugt das gleiche Ergebnis:

$str =~ s/(?:(\S)[\r\n]+)|(?:[\r\n]+\s+([\r\n])+)/$1/g;

Es ist keine spezielle Konfiguration oder Unterstützung für mehrere Leitungen erforderlich. Sie können jedoch das Flag s hinzufügen, wenn es obligatorisch ist.

$str =~ s/(?:(\S)[\r\n]+)|(?:[\r\n]+\s+([\r\n])+)/$1/sg;
1
Toto
string corrected = 
    System.Text.RegularExpressions.Regex.Replace(input, @"\n+", "\n");
1
Adam Robinson

String-Erweiterung 

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

Ich bin nicht sicher, ob es effizient ist, aber =)

  List<string> strList = myString.Split(new string[] { "\n" }, StringSplitOptions.None).ToList<string>();
  myString = string.Join("\n", strList.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().ToList());
0
albatross

Hier ist etwas ganz einfaches, wenn Sie gegen jede einzelne Zeile arbeiten ...

(^\s+|\s+|^)$
0
kgoedtel

Eh. Nun, nach all dem konnte ich keinen finden, der alle möglichen Fälle traf, die ich herausfinden konnte. Das Folgende ist meine letzte Beschwerde einer Regex, die sich strippt 

  1. Alle leeren Zeilen vom Anfang einer Zeichenfolge
    • Keine Leerzeichen am Anfang der ersten Nicht-Leerzeichen-Zeile
  2. Alle leeren Zeilen nach der ersten Zeile ohne Leerzeichen und vor der letzten Zeile ohne Leerzeichen
    • Dabei werden alle Leerzeichen am Anfang einer Nicht-Leerzeichen-Zeile beibehalten
  3. Alle leeren Zeilen nach der letzten Nicht-Whitespace-Zeile, einschließlich der letzten neuen Zeile

(? <= (\ r\n) | ^)\s *\r\n |\r\n\s * $

was im Wesentlichen sagt:

  • Gleich nach
    • Der Anfang der Zeichenfolge ODER
    • Das Ende der letzten Zeile
  • Passen Sie so viele zusammenhängende Leerzeichen wie möglich an, die in einer neuen Zeile enden *.
  • ODER
  • Entsprechende Zeilenumbrüche und so viele zusammenhängende Leerzeichen wie möglich, dass am Ende des Strings endet

Die erste Hälfte fängt alle Leerzeichen am Anfang der Zeichenfolge bis zur ersten Nicht-Leerzeichen-Zeile oder alle Leerzeichen zwischen Nicht-Leerzeichen-Zeilen. In der zweiten Hälfte wird der verbleibende Whitespace in der Zeichenfolge erfasst, einschließlich der neuen Zeile der letzten Zeile, die keine Whitespace-Zeile ist.

Danke an alle, die versucht haben zu helfen. Ihre Antworten haben mir geholfen, alles durchzudenken, was ich beim Matching beachten musste.

* (Dieser reguläre Ausdruck hält eine neue Zeile für \r\n und muss daher je nach Quelle der Zeichenfolge angepasst werden. Es müssen keine Optionen festgelegt werden, um die Übereinstimmung auszuführen.)

0
Will
char[] delimiters = new char[] { '\r', '\n' };
string[] lines = value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
string result = string.Join(Environment.NewLine, lines)
0
Ben Hoffstein