Diviser une chaîne qui comporte des espaces blancs, sauf s’ils sont entourés de «guillemets»?

Pour simplifier les choses:

ssortingng streamR = sr.ReadLine(); // sr.Readline results in one "two two" 

Je veux pouvoir les enregistrer en tant que deux chaînes différentes, supprimez tous les espaces SAUF pour les espaces entre guillemets. Par conséquent, ce dont j’ai besoin est:

 ssortingng 1 = one ssortingng 2 = two two 

Jusqu’à présent, ce que j’ai trouvé qui fonctionne est le code suivant, mais il supprime les espaces dans les guillemets.

 //streamR.ReadLine only has two ssortingngs ssortingng[] splitter = streamR.Split(' '); str1 = splitter[0]; // Only set str2 if the length is >1 str2 = splitter.Length > 1 ? splitter[1] : ssortingng.Empty; 

La sortie de ceci devient

 one two 

Je me suis penché sur l’ expression régulière pour diviser les espaces, sauf entre guillemets, mais je n’arrive pas à faire en sorte que regex fonctionne / comprenne le code, en particulier comment les diviser pour qu’ils soient deux chaînes différentes. Tous les codes là-bas me donnent une erreur de compilation (j’utilise System.Text.RegularExpressions )

 ssortingng input = "one \"two two\" three \"four four\" five six"; var parts = Regex.Matches(input, @"[\""].+?[\""]|[^ ]+") .Cast() .Select(m => m.Value) .ToList(); 

Vous pouvez même le faire sans Regex: une expression LINQ avec Ssortingng.Split peut faire le travail.

Vous pouvez diviser votre chaîne avant par " puis diviser seulement les éléments avec un index pair dans le tableau résultant par .

 var result = mySsortingng.Split('"') .Select((element, index) => index % 2 == 0 // If even index ? element.Split(new[] { ' ' }, SsortingngSplitOptions.RemoveEmptyEnsortinges) // Split the item : new ssortingng[] { element }) // Keep the entire item .SelectMany(element => element).ToList(); 

Pour la chaîne:

 This is a test for "Splitting a ssortingng" that has white spaces, unless they are "enclosed within quotes" 

Il donne le résultat:

 This is a test for Splitting a ssortingng that has white spaces, unless they are enclosed within quotes 

METTRE À JOUR

 ssortingng mySsortingng = "WordOne \"Word Two\""; var result = mySsortingng.Split('"') .Select((element, index) => index % 2 == 0 // If even index ? element.Split(new[] { ' ' }, SsortingngSplitOptions.RemoveEmptyEnsortinges) // Split the item : new ssortingng[] { element }) // Keep the entire item .SelectMany(element => element).ToList(); Console.WriteLine(result[0]); Console.WriteLine(result[1]); Console.ReadKey(); 

MISE À JOUR 2

Comment définissez-vous une partie citée de la chaîne?

Nous supposerons que la chaîne avant le premier " n’est pas citée.

Ensuite, la chaîne placée entre le premier " et avant le second " est citée. La chaîne entre le deuxième " et le troisième " n’est pas citée. La chaîne entre le troisième et le quasortingème est citée, …

La règle générale est la suivante: chaque chaîne entre le (2 * n-1) th (nombre impair) " et (2 * n) th (nombre pair) " est citée. (1)

Quelle est la relation avec Ssortingng.Split ?

Ssortingng.Split avec la valeur par défaut SsortingngSplitOption (définie par SsortingngSplitOption.None) crée une liste de 1 chaîne, puis ajoute une nouvelle chaîne dans la liste pour chaque caractère de fractionnement trouvé.

Donc, avant le premier " , la chaîne est à l’index 0 dans le tableau fractionné, entre le premier et le second " , la chaîne est à l’index 1 dans le tableau, entre le troisième et le quasortingème, l’index 2, …

La règle générale est la suivante: La chaîne entre le nième et (n + 1) th " est à l’index n dans le tableau. (2)

Le donné (1) et (2) , nous pouvons conclure que: La partie cotée est à l’index impair dans le tableau divisé.

Vous pouvez utiliser la classe TextFieldParser qui fait partie de l’espace de noms Microsoft.VisualBasic.FileIO . (Vous devrez append une référence à Microsoft.VisualBasic à votre projet.):

 ssortingng inputSsortingng = "This is \"a test\" of the parser."; using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(inputSsortingng))) { using (Microsoft.VisualBasic.FileIO.TextFieldParser tfp = new TextFieldParser(ms)) { tfp.Delimiters = new ssortingng[] { " " }; tfp.HasFieldsEnclosedInQuotes = true; ssortingng[] output = tfp.ReadFields(); for (int i = 0; i < output.Length; i++) { Console.WriteLine("{0}:{1}", i, output[i]); } } } 

Qui génère la sortie:

 0:This 1:is 2:a test 3:of 4:the 5:parser. 

Comme un parsingur personnalisé pourrait être plus approprié pour cela.

C’est quelque chose que j’ai écrit une fois lorsque j’avais une exigence d’parsing spécifique (et très étrange) qui impliquait des parenthèses et des espaces, mais il est suffisamment générique pour fonctionner avec pratiquement tous les délimiteurs et qualificateurs de texte.

 public static IEnumerable ParseText(Ssortingng line, Char delimiter, Char textQualifier) { if (line == null) yield break; else { Char prevChar = '\0'; Char nextChar = '\0'; Char currentChar = '\0'; Boolean inSsortingng = false; SsortingngBuilder token = new SsortingngBuilder(); for (int i = 0; i < line.Length; i++) { currentChar = line[i]; if (i > 0) prevChar = line[i - 1]; else prevChar = '\0'; if (i + 1 < line.Length) nextChar = line[i + 1]; else nextChar = '\0'; if (currentChar == textQualifier && (prevChar == '\0' || prevChar == delimiter) && !inString) { inString = true; continue; } if (currentChar == textQualifier && (nextChar == '\0' || nextChar == delimiter) && inString) { inString = false; continue; } if (currentChar == delimiter && !inString) { yield return token.ToString(); token = token.Remove(0, token.Length); continue; } token = token.Append(currentChar); } yield return token.ToString(); } } 

L'utilisation serait:

 var parsedText = ParseText(streamR, ' ', '"'); 

Il y a juste un petit problème avec la réponse de Squazz. Cela fonctionne pour sa chaîne, mais pas si vous ajoutez d’autres éléments. Par exemple

 ssortingng mySsortingng = "WordOne \"Word Two\" Three" 

Dans ce cas, la suppression du dernier guillemet nous donnerait 4 résultats, pas trois.

C’est facile à résoudre cependant, il suffit de compter le nombre de caractères d’évasion et, s’il est inégal, de supprimer le dernier (adapter selon vos besoins).

  public static List Split(this ssortingng mySsortingng, char separator, char escapeCharacter) { int nbEscapeCharactoers = mySsortingng.Count(c => c == escapeCharacter); if (nbEscapeCharactoers % 2 != 0) // uneven number of escape characters { int lastIndex = mySsortingng.LastIndexOf("" + escapeCharacter, SsortingngComparison.Ordinal); mySsortingng = mySsortingng.Remove(lastIndex, 1); // remove the last escape character } var result = mySsortingng.Split(escapeCharacter) .Select((element, index) => index % 2 == 0 // If even index ? element.Split(new[] { separator }, SsortingngSplitOptions.RemoveEmptyEnsortinges) // Split the item : new ssortingng[] { element }) // Keep the entire item .SelectMany(element => element).ToList(); return result; } 

Je l’ai également transformée en une méthode d’extension et rendu le séparateur et le caractère d’évasion configurables.

OP voulait

… supprime tous les espaces SAUF pour les espaces entre guillemets

La solution de Cédric Bignon a failli le faire, mais n’a pas pris en compte le fait qu’il pourrait y avoir un nombre inégal de guillemets. En commençant par vérifier cela, puis en supprimant les excès, vous vous assurez de ne plus vous séparer que si l’élément est réellement encapsulé par des guillemets.

 ssortingng mySsortingng = "WordOne \"Word Two"; int placement = mySsortingng.LastIndexOf("\"", SsortingngComparison.Ordinal); if (placement >= 0) mySsortingng = mySsortingng.Remove(placement, 1); var result = mySsortingng.Split('"') .Select((element, index) => index % 2 == 0 // If even index ? element.Split(new[] { ' ' }, SsortingngSplitOptions.RemoveEmptyEnsortinges) // Split the item : new ssortingng[] { element }) // Keep the entire item .SelectMany(element => element).ToList(); Console.WriteLine(result[0]); Console.WriteLine(result[1]); Console.ReadKey(); 

Le crédit de la logique revient à Cédric Bignon, j’ai seulement ajouté une sauvegarde.

Avec prise en charge des guillemets doubles.

Chaîne:

 a "bb" "c ""c"" c" 

Résultat:

 a "bb" "c ""c"" c" 

Code:

 var list=Regex.Matches(value, @"\""(\""\""|[^\""])+\""|[^ ]+", RegexOptions.ExplicitCapture) .Cast() .Select(m => m.Value) .ToList(); 

Suppression facultative des guillemets:

 Select(m => m.StartsWith("\"") ? m.Subssortingng(1, m.Length - 2).Replace("\"\"", "\"") : m) 

Résultat

 abb c "c" c