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.
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();
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();
}
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
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.
pas bon. Je voudrais utiliser celui-ci en utilisant JSON.net:
var o = JsonConvert.DeserializeObject(prettyJson);
new minifiedJson = JsonConvert.SerializeObject(o, Formatting.None);
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
.
(?<=\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).(?<=\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).(\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:
\r\n \r\n\t\r\n
)\r\n\r\n
)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.
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;
}
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();
}
}
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"
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;
string corrected =
System.Text.RegularExpressions.Regex.Replace(input, @"\n+", "\n");
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);
}
}
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());
Voici quelque chose de simple si vous travaillez contre chaque ligne individuelle ...
(^\s+|\s+|^)$
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
(? <= (\ r\n) | ^)\s *\r\n |\r\n\s * $
qui dit essentiellement:
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.)
char[] delimiters = new char[] { '\r', '\n' };
string[] lines = value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
string result = string.Join(Environment.NewLine, lines)