web-dev-qa-db-fra.com

Suppression efficace de toutes les lignes d'espacement d'une chaîne multiligne

En C #, quel est le meilleur moyen de supprimer les lignes vides, c'est-à-dire les lignes ne contenant que des espaces d'une chaîne? Je suis heureux d'utiliser un regex si c'est la meilleure solution.

EDIT: Je devrais ajouter que j'utilise .NET 2.0.


Mise à jour des primes: Je reviens sur cette récompense une fois la prime octroyée, mais je voulais clarifier certaines choses.

Tout d’abord, tout regex Perl 5 compat fonctionnera. Cela ne se limite pas aux développeurs .NET. Le titre et les balises ont été modifiés pour refléter cela.

Deuxièmement, bien que j'ai donné un exemple rapide dans les détails de la prime, ce n'est pas le test uniquement que vous devez satisfaire. Votre solution doit supprimer tout lignes qui ne sont constituées que de espaces, ainsi que la dernière nouvelle ligne. S'il existe une chaîne qui, après avoir parcouru votre expression rationnelle, se termine par "/ r/n" ou tous les caractères d'espacement, elle échoue. 

30
FunLovinCoder

Si vous souhaitez supprimer les lignes contenant des espaces (tabulations, espaces), essayez:

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

Edit (for @Will): La solution la plus simple pour supprimer les retours à la ligne consiste à utiliser TrimEnd sur la chaîne obtenue, par exemple:

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

du haut de ma tête...

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

tourne ceci:

 fdasdf 
 asdf 
 [tabs] 

 [espaces] 

 asdf 

.__

dans ceci:

 fdasdf 
 asdf 
 asdf 
13
Sky Sanders

Utiliser LINQ:

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

Si vous avez des entrées volumineuses et/ou des fins de ligne incohérentes, vous devez utiliser un StringReader et faire la vieille école ci-dessus avec une boucle foreach.

8
dtb

pas bon. Je voudrais utiliser celui-ci en utilisant JSON.net:

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

Très bien, cette réponse est conforme aux exigences clarifiées spécifiées dans la prime:

J'ai également besoin de supprimer les retours à la ligne, et mon Regex-fu est échouer. Ma récompense va à tous ceux qui peuvent me donner une regex qui passe ce test: StripWhitespace ("test\r\n\r\nthis\r\n\r\n") == "test\r\ncette"

Alors voici la réponse:

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

Ou dans le code C # fourni par @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);

Essayons maintenant de le comprendre. Il y a trois modèles facultatifs ici que je suis prêt à remplacer par string.empty.

  1. (?<=\r?\n)(\s*$\r?\n)+ - correspond à un nombre illimité de lignes contenant uniquement des espaces et précédées d'un saut de ligne (mais ne correspond pas aux premiers sauts de ligne précédents).
  2. (?<=\r?\n)(\r?\n)+ - correspond à un nombre illimité de lignes vides sans contenu précédé d'un saut de ligne (mais ne correspond pas aux premiers sauts de ligne précédents).
  3. (\r?\n)+\z - correspond à un saut de ligne illimité à la fin de la chaîne testée (le saut de ligne final est comme vous l'avez appelé)

Cela répond parfaitement à votre test! Mais satisfait également les styles de sauts de ligne \r\n et \n! Testez-le! Je crois que ce sera la réponse la plus correcte. Bien qu'une expression plus simple réussisse votre test de prime spécifié, cette expression rationnelle passe des conditions plus complexes.

EDIT: @Will a signalé un défaut potentiel dans la dernière correspondance de motif de la regex ci-dessus, en ce sens qu'il ne correspondrait pas à plusieurs sauts de ligne contenant des espaces à la fin de la chaîne de test. Alors changeons ce dernier motif en ceci: 

\b\s+\z Le\b est une limite de Word (début ou fin d'un mot), le\s + est un ou plusieurs espaces blancs, le\z est la fin de la chaîne de test (fin du "fichier"). Alors maintenant, il correspondra à tout assortiment d'espaces à la fin du fichier, y compris les tabulations et les espaces, en plus des retours à la ligne et des sauts de ligne. J'ai testé les deux cas de test fournis par @ Will. 

Donc, tous ensemble maintenant, cela devrait être:

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

EDIT # 2: Bon, il y a un autre cas possible @Wil a découvert que la dernière expression régulière ne couvre pas. Ce cas est constitué d’entrées comportant des sauts de ligne au début du fichier avant tout contenu. Ajoutons donc un autre motif correspondant au début du fichier.

\A\s+ - Le \A correspond au début du fichier, le \s+ correspond à un ou plusieurs espaces blancs.

Alors maintenant nous avons:

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

Alors maintenant, nous avons quatre modèles pour l'appariement:

  1. les espaces au début du fichier,
  2. sauts de ligne redondants contenant des espaces, (ex: \r\n \r\n\t\r\n)
  3. sauts de ligne redondants sans contenu, (ex: \r\n\r\n)
  4. espaces blancs à la fin du fichier
3
BenSwayne

En réponse à la prime de Will, qui attend une solution prenant "test\r\n \r\nthis\r\n\r\n" et générant "test\r\nthis", j'ai proposé une solution qui utilise groupement atomique } (alias nonbacktracking Subexpressions sur MSDN) . Je recommande de lire ces articles pour mieux comprendre ce qui se passe. En fin de compte, le groupe atomique a aidé à faire correspondre les derniers caractères de nouvelle ligne laissés de côté.

Utilisez RegexOptions.Multiline avec ce modèle:

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

Voici un exemple avec quelques cas de test, dont certains que j'ai recueillis à partir des commentaires de Will sur d'autres articles, ainsi que le mien.

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

ÉDITER: pour résoudre le problème de l'échec du modèle à nettoyer le texte avec une combinaison d'espaces et de nouvelles lignes, j'ai ajouté \s* à la dernière portion d'alternance de l'expression rationnelle. Mon modèle précédent était redondant et j'ai réalisé que \s* gèrerait les deux cas.

2
Ahmad Mageed

Voici une autre option: utilisez la classe StringReader. Avantages: un passage sur la chaîne ne crée aucun tableau intermédiaire.

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

Remarque: la méthode IsNullOrWhiteSpace est nouvelle dans .NET 4.0 . Si vous ne l'avez pas, c'est trivial d'écrire par vous-même:

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

Je vais avec:

  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

si ses seuls espaces blancs, pourquoi ne pas utiliser la méthode de chaîne C # 

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

le résultat sera "AOPV1.5"

1
dnxit

En réponse à la prime de Will, voici un sous-programme Perl qui donne une réponse correcte au scénario de test:

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

sortie:

'test

this

'
'test
this'

Afin de ne pas utiliser \R, remplacez-le par [\r\n] et inversez l'alternative. Celui-ci produit le même résultat:

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

Il n'y a pas besoin de configuration spéciale ni de support multi-lignes. Néanmoins, vous pouvez ajouter s flag si c'est obligatoire.

$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

Extension de chaîne 

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

Je ne suis pas sûr que ce soit efficace mais =)

  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

Voici quelque chose de simple si vous travaillez contre chaque ligne individuelle ...

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

Eh. Eh bien, après tout cela, je ne pouvais pas en trouver un qui ferait l'affaire dans tous les cas difficiles à comprendre. Ce qui suit est ma dernière incantation d’une regex qui dépouille 

  1. Toutes les lignes vides à partir du début d'une chaîne
    • N'incluant pas d'espaces au début de la première ligne non-blanche
  2. Toutes les lignes vides après la première ligne non-blanche et avant la dernière ligne non-blanche
    • Encore une fois, en préservant tous les espaces au début de toute ligne non-blanche
  3. Toutes les lignes vides après la dernière ligne non-blanc, y compris la dernière ligne récente

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

qui dit essentiellement:

  • Juste après
    • Le début de la chaîne OU
    • La fin de la dernière ligne
  • Faites correspondre autant que possible les espaces contigus que se termine par une nouvelle ligne *
  • OU
  • Correspond à une nouvelle ligne et à autant d’espaces contigus que possible qui se termine à la fin de la chaîne

La première moitié intercepte tous les espaces au début de la chaîne jusqu'à la première ligne sans espace, ou tous les espaces entre deux lignes. La seconde moitié arrache les espaces blancs restants dans la chaîne, y compris le retour à la ligne de la dernière ligne non-blancs.

Merci à tous ceux qui ont essayé d’aider; Vos réponses m'ont aidé à réfléchir à tout ce que je devais prendre en compte lors de l'appariement.

* (Cette expression rationnelle considère une nouvelle ligne comme étant \r\n et devra donc être ajustée en fonction de la source de la chaîne. Aucune option ne doit être définie pour que la correspondance soit exécutée.)

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