Disons que j'ai une chaîne telle que:
"Hello how are you doing?"
Je voudrais une fonction qui transforme plusieurs espaces en un seul espace.
Donc j'obtiendrais:
"Hello how are you doing?"
Je sais que je pourrais utiliser regex ou appeler
string s = "Hello how are you doing?".replace(" "," ");
Mais je devrais l'appeler plusieurs fois pour m'assurer que tous les espaces séquentiels sont remplacés par un seul.
Existe-t-il déjà une méthode intégrée pour cela?
string cleanedString = System.Text.RegularExpressions.Regex.Replace(dirtyString,@"\s+"," ");
Cette question n’est pas aussi simple que le prétendent d’autres affiches (et comme je le croyais au départ), car elle n’est pas aussi précise qu’elle le devrait.
Il y a une différence entre "espace" et "espace". Si vous seulement signifie des espaces, vous devriez utiliser une expression rationnelle de " {2,}"
. Si vous voulez dire des espaces , c'est autre chose. Est-ce que tous les espaces doivent être convertis en espaces? Que devrait-il arriver à l'espace au début et à la fin?
Pour le repère ci-dessous, j'ai supposé que vous ne vous souciez que des espaces et que vous ne vouliez rien faire pour les espaces simples, même au début et à la fin.
Notez que la correction est presque toujours plus importante que la performance. Le fait que la solution Split/Join supprime tous les espaces de début/fin (même les espaces simples) est incorrect en ce qui concerne vos exigences spécifiées (qui peuvent être incomplètes, bien sûr).
Le benchmark utilise MiniBench .
using System;
using System.Text.RegularExpressions;
using MiniBench;
internal class Program
{
public static void Main(string[] args)
{
int size = int.Parse(args[0]);
int gapBetweenExtraSpaces = int.Parse(args[1]);
char[] chars = new char[size];
for (int i=0; i < size/2; i += 2)
{
// Make sure there actually *is* something to do
chars[i*2] = (i % gapBetweenExtraSpaces == 1) ? ' ' : 'x';
chars[i*2 + 1] = ' ';
}
// Just to make sure we don't have a \0 at the end
// for odd sizes
chars[chars.Length-1] = 'y';
string bigString = new string(chars);
// Assume that one form works :)
string normalized = NormalizeWithSplitAndJoin(bigString);
var suite = new TestSuite<string, string>("Normalize")
.Plus(NormalizeWithSplitAndJoin)
.Plus(NormalizeWithRegex)
.RunTests(bigString, normalized);
suite.Display(ResultColumns.All, suite.FindBest());
}
private static readonly Regex MultipleSpaces =
new Regex(@" {2,}", RegexOptions.Compiled);
static string NormalizeWithRegex(string input)
{
return MultipleSpaces.Replace(input, " ");
}
// Guessing as the post doesn't specify what to use
private static readonly char[] Whitespace =
new char[] { ' ' };
static string NormalizeWithSplitAndJoin(string input)
{
string[] split = input.Split
(Whitespace, StringSplitOptions.RemoveEmptyEntries);
return string.Join(" ", split);
}
}
Quelques essais:
c:\Users\Jon\Test>test 1000 50
============ Normalize ============
NormalizeWithSplitAndJoin 1159091 0:30.258 22.93
NormalizeWithRegex 26378882 0:30.025 1.00
c:\Users\Jon\Test>test 1000 5
============ Normalize ============
NormalizeWithSplitAndJoin 947540 0:30.013 1.07
NormalizeWithRegex 1003862 0:29.610 1.00
c:\Users\Jon\Test>test 1000 1001
============ Normalize ============
NormalizeWithSplitAndJoin 1156299 0:29.898 21.99
NormalizeWithRegex 23243802 0:27.335 1.00
Ici, le premier nombre est le nombre d'itérations, le second le temps pris et le troisième est un score mis à l'échelle, 1,0 étant le meilleur.
Cela montre que dans au moins certains cas (y compris celui-ci), une expression régulière peut dépasser la solution Fractionner/Joindre, parfois avec une marge très significative.
Toutefois, si vous optez pour une configuration "Tous les espaces", Split/Join apparaît comme gagnant. Comme souvent, le diable se cache dans les détails ...
Bien que les réponses existantes soient correctes, je voudrais souligner une approche qui ne fonctionne pas ne fonctionne pas:
public static string DontUseThisToCollapseSpaces(string text)
{
while (text.IndexOf(" ") != -1)
{
text = text.Replace(" ", " ");
}
return text;
}
Cela peut boucler pour toujours. Quelqu'un veut-il deviner pourquoi? (Je ne suis tombé sur cette question que lorsque cela a été posé comme question de groupe de discussion il y a quelques années ... quelqu'un a en fait eu un problème.)
Une expression régulière serait le moyen le plus simple. Si vous écrivez l'expression rationnelle correctement, vous n'aurez pas besoin de plusieurs appels.
Changez-le en ceci:
string s = System.Text.RegularExpressions.Regex.Replace(s, @"\s{2,}", " ");
Voici la solution avec laquelle je travaille. Sans RegEx et String.Split.
public static string TrimWhiteSpace(this string Value)
{
StringBuilder sbOut = new StringBuilder();
if (!string.IsNullOrEmpty(Value))
{
bool IsWhiteSpace = false;
for (int i = 0; i < Value.Length; i++)
{
if (char.IsWhiteSpace(Value[i])) //Comparion with WhiteSpace
{
if (!IsWhiteSpace) //Comparison with previous Char
{
sbOut.Append(Value[i]);
IsWhiteSpace = true;
}
}
else
{
IsWhiteSpace = false;
sbOut.Append(Value[i]);
}
}
}
return sbOut.ToString();
}
afin que vous puissiez:
string cleanedString = dirtyString.TrimWhiteSpace();
Comme déjà indiqué, cela se fait facilement par une expression régulière. J'ajouterai simplement que vous pourriez vouloir ajouter un .trim () à cela pour vous débarrasser des espaces de début/fin.
Je partage ce que j'utilise, car il semble que j'ai proposé quelque chose de différent. Je l'utilise depuis un moment et c'est assez rapide pour moi. Je ne sais pas comment ça se compare aux autres. Je l'utilise dans un graveur de fichier délimité et exécute des champs de données volumineux, un champ à la fois.
public static string NormalizeWhiteSpace(string S)
{
string s = S.Trim();
bool iswhite = false;
int iwhite;
int sLength = s.Length;
StringBuilder sb = new StringBuilder(sLength);
foreach(char c in s.ToCharArray())
{
if(Char.IsWhiteSpace(c))
{
if (iswhite)
{
//Continuing whitespace ignore it.
continue;
}
else
{
//New WhiteSpace
//Replace whitespace with a single space.
sb.Append(" ");
//Set iswhite to True and any following whitespace will be ignored
iswhite = true;
}
}
else
{
sb.Append(c.ToString());
//reset iswhitespace to false
iswhite = false;
}
}
return sb.ToString();
}
Un moyen rapide de supprimer les blancs ... C'est le plus rapide, basé sur la copie in-situ de Felipe Machado.
static string InPlaceCharArray(string str)
{
var len = str.Length;
var src = str.ToCharArray();
int dstIdx = 0;
bool lastWasWS = false;
for (int i = 0; i < len; i++)
{
var ch = src[i];
if (src[i] == '\u0020')
{
if (lastWasWS == false)
{
src[dstIdx++] = ch;
lastWasWS = true;
}
}
else
{
lastWasWS = false;
src[dstIdx++] = ch;
}
}
return new string(src, 0, dstIdx);
}
Les repères ...
InPlaceCharArraySpaceOnly par Felipe Machado sur CodeProject 2015 et modifié par Sunsetquest pour la suppression d'espaces multiples. Heure: 3,75 ticks
InPlaceCharArray de Felipe Machado 2015 et légèrement modifié par Sunsetquest pour la suppression de plusieurs espaces. Heure 6.50 Ticks (prend également en charge les onglets)
SplitAndJoinOnSpace par Jon Skeet . Heure: 13.25 Ticks
StringBuilder by fubo Heure: 13.5 ticks (prend également en charge les onglets)
Regex avec compiler par Jon Skeet . Temps: 17 ticks
StringBuilder by David S 2013 Heure: 30.5 ticks
Regex avec non-compile by Brandon Heure: 63.25 Ticks
StringBuilder by ser214147 Heure: 77.125 Ticks
Regex avec non-compilation Tim Hoolihan Time: 147.25 Ticks
Le code de référence ...
using System;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;
using System.Text;
static class Program
{
public static void Main(string[] args)
{
long seed = ConfigProgramForBenchmarking();
Stopwatch sw = new Stopwatch();
string warmup = "This is a Warm up function for best benchmark results." + seed;
string input1 = "Hello World, how are you doing?" + seed;
string input2 = "It\twas\t \tso Nice to\t\t see you \tin 1950. \t" + seed;
string correctOutput1 = "Hello World, how are you doing?" + seed;
string correctOutput2 = "It\twas\tso Nice to\tsee you in 1950. " + seed;
string output1,output2;
//warm-up timer function
sw.Restart();
sw.Stop();
sw.Restart();
sw.Stop();
long baseVal = sw.ElapsedTicks;
// InPlace Replace by Felipe Machado but modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
output1 = InPlaceCharArraySpaceOnly (warmup);
sw.Restart();
output1 = InPlaceCharArraySpaceOnly (input1);
output2 = InPlaceCharArraySpaceOnly (input2);
sw.Stop();
Console.WriteLine("InPlaceCharArraySpaceOnly : " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
// InPlace Replace by Felipe R. Machado and slightly modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
output1 = InPlaceCharArray(warmup);
sw.Restart();
output1 = InPlaceCharArray(input1);
output2 = InPlaceCharArray(input2);
sw.Stop();
Console.WriteLine("InPlaceCharArray: " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
//Regex with non-compile Tim Hoolihan (https://stackoverflow.com/a/1279874/2352507)
string cleanedString =
output1 = Regex.Replace(warmup, @"\s+", " ");
sw.Restart();
output1 = Regex.Replace(input1, @"\s+", " ");
output2 = Regex.Replace(input2, @"\s+", " ");
sw.Stop();
Console.WriteLine("Regex by Tim Hoolihan: " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
//Regex with compile by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
output1 = MultipleSpaces.Replace(warmup, " ");
sw.Restart();
output1 = MultipleSpaces.Replace(input1, " ");
output2 = MultipleSpaces.Replace(input2, " ");
sw.Stop();
Console.WriteLine("Regex with compile by Jon Skeet: " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
//Split And Join by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
output1 = SplitAndJoinOnSpace(warmup);
sw.Restart();
output1 = SplitAndJoinOnSpace(input1);
output2 = SplitAndJoinOnSpace(input2);
sw.Stop();
Console.WriteLine("Split And Join by Jon Skeet: " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
//Regex by Brandon (https://stackoverflow.com/a/1279878/2352507
output1 = Regex.Replace(warmup, @"\s{2,}", " ");
sw.Restart();
output1 = Regex.Replace(input1, @"\s{2,}", " ");
output2 = Regex.Replace(input2, @"\s{2,}", " ");
sw.Stop();
Console.WriteLine("Regex by Brandon: " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
//StringBuilder by user214147 (https://stackoverflow.com/a/2156660/2352507
output1 = user214147(warmup);
sw.Restart();
output1 = user214147(input1);
output2 = user214147(input2);
sw.Stop();
Console.WriteLine("StringBuilder by user214147: " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
//StringBuilder by fubo (https://stackoverflow.com/a/27502353/2352507
output1 = fubo(warmup);
sw.Restart();
output1 = fubo(input1);
output2 = fubo(input2);
sw.Stop();
Console.WriteLine("StringBuilder by fubo: " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
//StringBuilder by David S 2013 (https://stackoverflow.com/a/16035044/2352507)
output1 = SingleSpacedTrim(warmup);
sw.Restart();
output1 = SingleSpacedTrim(input1);
output2 = SingleSpacedTrim(input2);
sw.Stop();
Console.WriteLine("StringBuilder(SingleSpacedTrim) by David S: " + (sw.ElapsedTicks - baseVal));
Console.WriteLine(" Trial1:(spaces only) " + (output1 == correctOutput1 ? "PASS " : "FAIL "));
Console.WriteLine(" Trial2:(spaces+tabs) " + (output2 == correctOutput2 ? "PASS " : "FAIL "));
}
// InPlace Replace by Felipe Machado and slightly modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
static string InPlaceCharArray(string str)
{
var len = str.Length;
var src = str.ToCharArray();
int dstIdx = 0;
bool lastWasWS = false;
for (int i = 0; i < len; i++)
{
var ch = src[i];
if (src[i] == '\u0020')
{
if (lastWasWS == false)
{
src[dstIdx++] = ch;
lastWasWS = true;
}
}
else
{
lastWasWS = false;
src[dstIdx++] = ch;
}
}
return new string(src, 0, dstIdx);
}
// InPlace Replace by Felipe R. Machado but modified by Ryan for multi-space removal (http://www.codeproject.com/Articles/1014073/Fastest-method-to-remove-all-whitespace-from-Strin)
static string InPlaceCharArraySpaceOnly (string str)
{
var len = str.Length;
var src = str.ToCharArray();
int dstIdx = 0;
bool lastWasWS = false; //Added line
for (int i = 0; i < len; i++)
{
var ch = src[i];
switch (ch)
{
case '\u0020': //SPACE
case '\u00A0': //NO-BREAK SPACE
case '\u1680': //OGHAM SPACE MARK
case '\u2000': // EN QUAD
case '\u2001': //EM QUAD
case '\u2002': //EN SPACE
case '\u2003': //EM SPACE
case '\u2004': //THREE-PER-EM SPACE
case '\u2005': //FOUR-PER-EM SPACE
case '\u2006': //SIX-PER-EM SPACE
case '\u2007': //FIGURE SPACE
case '\u2008': //PUNCTUATION SPACE
case '\u2009': //THIN SPACE
case '\u200A': //HAIR SPACE
case '\u202F': //NARROW NO-BREAK SPACE
case '\u205F': //MEDIUM MATHEMATICAL SPACE
case '\u3000': //IDEOGRAPHIC SPACE
case '\u2028': //LINE SEPARATOR
case '\u2029': //PARAGRAPH SEPARATOR
case '\u0009': //[ASCII Tab]
case '\u000A': //[ASCII Line Feed]
case '\u000B': //[ASCII Vertical Tab]
case '\u000C': //[ASCII Form Feed]
case '\u000D': //[ASCII Carriage Return]
case '\u0085': //NEXT LINE
if (lastWasWS == false) //Added line
{
src[dstIdx++] = ch; //Added line
lastWasWS = true; //Added line
}
continue;
default:
lastWasWS = false; //Added line
src[dstIdx++] = ch;
break;
}
}
return new string(src, 0, dstIdx);
}
static readonly Regex MultipleSpaces =
new Regex(@" {2,}", RegexOptions.Compiled);
//Split And Join by Jon Skeet (https://stackoverflow.com/a/1280227/2352507)
static string SplitAndJoinOnSpace(string input)
{
string[] split = input.Split(new char[] { ' '}, StringSplitOptions.RemoveEmptyEntries);
return string.Join(" ", split);
}
//StringBuilder by user214147 (https://stackoverflow.com/a/2156660/2352507
public static string user214147(string S)
{
string s = S.Trim();
bool iswhite = false;
int iwhite;
int sLength = s.Length;
StringBuilder sb = new StringBuilder(sLength);
foreach (char c in s.ToCharArray())
{
if (Char.IsWhiteSpace(c))
{
if (iswhite)
{
//Continuing whitespace ignore it.
continue;
}
else
{
//New WhiteSpace
//Replace whitespace with a single space.
sb.Append(" ");
//Set iswhite to True and any following whitespace will be ignored
iswhite = true;
}
}
else
{
sb.Append(c.ToString());
//reset iswhitespace to false
iswhite = false;
}
}
return sb.ToString();
}
//StringBuilder by fubo (https://stackoverflow.com/a/27502353/2352507
public static string fubo(this string Value)
{
StringBuilder sbOut = new StringBuilder();
if (!string.IsNullOrEmpty(Value))
{
bool IsWhiteSpace = false;
for (int i = 0; i < Value.Length; i++)
{
if (char.IsWhiteSpace(Value[i])) //Comparison with WhiteSpace
{
if (!IsWhiteSpace) //Comparison with previous Char
{
sbOut.Append(Value[i]);
IsWhiteSpace = true;
}
}
else
{
IsWhiteSpace = false;
sbOut.Append(Value[i]);
}
}
}
return sbOut.ToString();
}
//David S. 2013 (https://stackoverflow.com/a/16035044/2352507)
public static String SingleSpacedTrim(String inString)
{
StringBuilder sb = new StringBuilder();
Boolean inBlanks = false;
foreach (Char c in inString)
{
switch (c)
{
case '\r':
case '\n':
case '\t':
case ' ':
if (!inBlanks)
{
inBlanks = true;
sb.Append(' ');
}
continue;
default:
inBlanks = false;
sb.Append(c);
break;
}
}
return sb.ToString().Trim();
}
/// <summary>
/// We want to run this item with max priory to lower the odds of
/// the OS from doing program context switches in the middle of our code.
/// source:https://stackoverflow.com/a/16157458
/// </summary>
/// <returns>random seed</returns>
private static long ConfigProgramForBenchmarking()
{
//prevent the JIT Compiler from optimizing Fkt calls away
long seed = Environment.TickCount;
//use the second Core/Processor for the test
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(2);
//prevent "Normal" Processes from interrupting Threads
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
//prevent "Normal" Threads from interrupting this thread
Thread.CurrentThread.Priority = ThreadPriority.Highest;
return seed;
}
}
Notes de référence: mode de publication, aucun débogueur attaché, processeur i7, moyenne de 4 exécutions, seules les chaînes courtes sont testées
VB.NET
Linha.Split(" ").ToList().Where(Function(x) x <> " ").ToArray
C #
Linha.Split(" ").ToList().Where(x => x != " ").ToArray();
Profitez de la puissance de LINQ = D
En utilisant le programme de test que Jon Skeet a posté, j'ai essayé de voir si je pouvais obtenir une boucle manuscrite pour courir plus vite.
Je peux battre NormalizeWithSplitAndJoin à chaque fois, mais seulement battre NormalizeWithRegex avec des entrées de 1000, 5.
static string NormalizeWithLoop(string input)
{
StringBuilder output = new StringBuilder(input.Length);
char lastChar = '*'; // anything other then space
for (int i = 0; i < input.Length; i++)
{
char thisChar = input[i];
if (!(lastChar == ' ' && thisChar == ' '))
output.Append(thisChar);
lastChar = thisChar;
}
return output.ToString();
}
Je n'ai pas examiné le code machine produit par la gigue, mais je suppose que le problème tient au temps pris par l'appel de StringBuilder.Append () et qu'il faudrait faire beaucoup mieux nécessiterait l'utilisation de code non sécurisé.
Donc Regex.Replace () est très rapide et difficile à battre !!
Regex regex = new Regex(@"\W+");
string outputString = regex.Replace(inputString, " ");
La plus petite solution:
var regExp =/\ s +/g, newString = oldString.replace (regExp, '');
Vous pouvez essayer ceci:
/// <summary>
/// Remove all extra spaces and tabs between words in the specified string!
/// </summary>
/// <param name="str">The specified string.</param>
public static string RemoveExtraSpaces(string str)
{
str = str.Trim();
StringBuilder sb = new StringBuilder();
bool space = false;
foreach (char c in str)
{
if (char.IsWhiteSpace(c) || c == (char)9) { space = true; }
else { if (space) { sb.Append(' '); }; sb.Append(c); space = false; };
}
return sb.ToString();
}