Ответ 1
Это самый быстрый способ, о котором я знаю, хотя вы сказали, что не хотите использовать регулярные выражения:
Regex.Replace(XML, @"\s+", "")
Я вызываю REST API и получаю ответ XML. Он возвращает список имен рабочей области, и я пишу быстрый метод IsExistingWorkspace()
. Поскольку все рабочие пространства состоят из смежных символов без пробелов, я предполагаю, что самый простой способ выяснить, есть ли конкретное рабочее пространство в списке, - удалить все пробелы (включая новые строки) и сделать это (XML - это строка, полученная из Интернета запрос):
XML.Contains("<name>" + workspaceName + "</name>");
Я знаю, что он чувствителен к регистру, и я полагаюсь на это. Мне просто нужен способ эффективно удалить все пробелы в строке. Я знаю, что RegEx и LINQ могут это сделать, но я открыт для других идей. Меня больше всего беспокоит скорость.
Это самый быстрый способ, о котором я знаю, хотя вы сказали, что не хотите использовать регулярные выражения:
Regex.Replace(XML, @"\s+", "")
У меня есть альтернативный способ без regexp, и, похоже, он работает очень хорошо. Это продолжение ответа Брэндона Моретца:
public static string RemoveWhitespace(this string input)
{
return new string(input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.ToArray());
}
Я протестировал его в простой unit test:
[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace1(string input, string expected)
{
string s = null;
for (int i = 0; i < 1000000; i++)
{
s = input.RemoveWhitespace();
}
Assert.AreEqual(expected, s);
}
[Test]
[TestCase("123 123 1adc \n 222", "1231231adc222")]
public void RemoveWhiteSpace2(string input, string expected)
{
string s = null;
for (int i = 0; i < 1000000; i++)
{
s = Regex.Replace(input, @"\s+", "");
}
Assert.AreEqual(expected, s);
}
Для 1,000,000 попыток первый вариант (без регулярного выражения) выполняется менее чем за секунду (700 мкс на моей машине), а второй занимает 3,5 секунды.
Попробуйте метод замены строки в С#.
XML.Replace(" ", string.Empty);
Мое решение - использовать Split и Join, и это удивительно быстро, на самом деле самый быстрый из лучших ответов здесь.
str = string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
Сроки для 10000 циклов на простой строке с пробелом в новых строках и вкладках
Улучшите это, завернув его в метод, чтобы придать ему значение, а также сделайте его методом расширения, пока мы на нем...
public static string RemoveWhitespace(this string str) {
return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
Опираясь на ответ Хенкса, я создал несколько тестовых методов с его ответом и несколько добавленных, более оптимизированных методов. Я обнаружил, что результаты отличаются в зависимости от размера входной строки. Поэтому я проверил с двумя наборами результатов. В самом быстром способе связанный источник имеет еще более быстрый путь. Но, поскольку это характеризуется как небезопасный, я оставил это.
Длинная строка входных результатов:
Короткие результаты ввода строки:
Код:
public class RemoveWhitespace
{
public static string RemoveStringReader(string input)
{
var s = new StringBuilder(input.Length); // (input.Length);
using (var reader = new StringReader(input))
{
int i = 0;
char c;
for (; i < input.Length; i++)
{
c = (char)reader.Read();
if (!char.IsWhiteSpace(c))
{
s.Append(c);
}
}
}
return s.ToString();
}
public static string RemoveLinqNativeCharIsWhitespace(string input)
{
return new string(input.ToCharArray()
.Where(c => !char.IsWhiteSpace(c))
.ToArray());
}
public static string RemoveLinq(string input)
{
return new string(input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.ToArray());
}
public static string RemoveRegex(string input)
{
return Regex.Replace(input, @"\s+", "");
}
private static Regex compiled = new Regex(@"\s+", RegexOptions.Compiled);
public static string RemoveRegexCompiled(string input)
{
return compiled.Replace(input, "");
}
public static string RemoveForLoop(string input)
{
for (int i = input.Length - 1; i >= 0; i--)
{
if (char.IsWhiteSpace(input[i]))
{
input = input.Remove(i, 1);
}
}
return input;
}
public static string StringSplitThenJoin(this string str)
{
return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
}
public static string RemoveInPlaceCharArray(string input)
{
var len = input.Length;
var src = input.ToCharArray();
int dstIdx = 0;
for (int i = 0; i < len; i++)
{
var ch = src[i];
switch (ch)
{
case '\u0020':
case '\u00A0':
case '\u1680':
case '\u2000':
case '\u2001':
case '\u2002':
case '\u2003':
case '\u2004':
case '\u2005':
case '\u2006':
case '\u2007':
case '\u2008':
case '\u2009':
case '\u200A':
case '\u202F':
case '\u205F':
case '\u3000':
case '\u2028':
case '\u2029':
case '\u0009':
case '\u000A':
case '\u000B':
case '\u000C':
case '\u000D':
case '\u0085':
continue;
default:
src[dstIdx++] = ch;
break;
}
}
return new string(src, 0, dstIdx);
}
}
Тесты:
[TestFixture]
public class Test
{
// Short input
//private const string input = "123 123 \t 1adc \n 222";
//private const string expected = "1231231adc222";
// Long input
private const string input = "123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222123 123 \t 1adc \n 222";
private const string expected = "1231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc2221231231adc222";
private const int iterations = 1000000;
[Test]
public void RemoveInPlaceCharArray()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveInPlaceCharArray(input);
}
stopwatch.Stop();
Console.WriteLine("InPlaceCharArray: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveStringReader()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveStringReader(input);
}
stopwatch.Stop();
Console.WriteLine("String reader: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveLinqNativeCharIsWhitespace()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveLinqNativeCharIsWhitespace(input);
}
stopwatch.Stop();
Console.WriteLine("LINQ using native char.IsWhitespace: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveLinq()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveLinq(input);
}
stopwatch.Stop();
Console.WriteLine("LINQ: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveRegex()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveRegex(input);
}
stopwatch.Stop();
Console.WriteLine("Regex: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveRegexCompiled()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveRegexCompiled(input);
}
stopwatch.Stop();
Console.WriteLine("RegexCompiled: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[Test]
public void RemoveForLoop()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.RemoveForLoop(input);
}
stopwatch.Stop();
Console.WriteLine("ForLoop: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
[TestMethod]
public void StringSplitThenJoin()
{
string s = null;
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
s = RemoveWhitespace.StringSplitThenJoin(input);
}
stopwatch.Stop();
Console.WriteLine("StringSplitThenJoin: " + stopwatch.ElapsedMilliseconds + " ms");
Assert.AreEqual(expected, s);
}
}
Редактировать: Протестирован хороший лайнер от Kernowcode.
Просто альтернатива, потому что это выглядит довольно хорошо:) - ПРИМЕЧАНИЕ: Ответ Henks является самым быстрым из них.
input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.Select(c => c.ToString())
.Aggregate((a, b) => a + b);
Тестирование 1 000 000 циклов на "This is a simple Test"
Этот метод = 1,74 секунды
Regex = 2.58 секунд new String
(Хенкс) = 0,82
Я нашел хорошую статью об этом в CodeProject от Фелипе Мачадо (с помощью Ричарда Робертсона)
Он проверил десять разных методов. Это самая быстрая и небезопасная версия...
public static unsafe string TrimAllWithStringInplace(string str) {
fixed (char* pfixed = str) {
char* dst = pfixed;
for (char* p = pfixed; *p != 0; p++)
switch (*p) {
case '\u0020': case '\u00A0': case '\u1680': case '\u2000': case '\u2001':
case '\u2002': case '\u2003': case '\u2004': case '\u2005': case '\u2006':
case '\u2007': case '\u2008': case '\u2009': case '\u200A': case '\u202F':
case '\u205F': case '\u3000': case '\u2028': case '\u2029': case '\u0009':
case '\u000A': case '\u000B': case '\u000C': case '\u000D': case '\u0085':
continue;
default:
*dst++ = *p;
break;
}
return new string(pfixed, 0, (int)(dst - pfixed));
}
И самая быстрая безопасная версия...
public static string TrimAllWithInplaceCharArray(string str) {
var len = str.Length;
var src = str.ToCharArray();
int dstIdx = 0;
for (int i = 0; i < len; i++) {
var ch = src[i];
switch (ch) {
case '\u0020': case '\u00A0': case '\u1680': case '\u2000': case '\u2001':
case '\u2002': case '\u2003': case '\u2004': case '\u2005': case '\u2006':
case '\u2007': case '\u2008': case '\u2009': case '\u200A': case '\u202F':
case '\u205F': case '\u3000': case '\u2028': case '\u2029': case '\u0009':
case '\u000A': case '\u000B': case '\u000C': case '\u000D': case '\u0085':
continue;
default:
src[dstIdx++] = ch;
break;
}
}
return new string(src, 0, dstIdx);
}
Есть также несколько хороших независимых тестов от Stian Standahl, которые также показывают, что функция Felipe примерно на 300% быстрее, чем следующая самая быстрая функция.
Если вам нужна превосходная производительность, вы должны избегать LINQ и регулярных выражений в этом случае. Я сделал некоторый бенчмаркинг производительности, и кажется, что если вы хотите удалить пустое пространство от начала и конца строки, string.Trim() - ваша конечная функция.
Если вам нужно удалить все пробелы из строки, следующий способ работает быстрее всего, что было опубликовано здесь:
public static string RemoveWhitespace(this string input)
{
int j = 0, inputlen = input.Length;
char[] newarr = new char[inputlen];
for (int i = 0; i < inputlen; ++i)
{
char tmp = input[i];
if (!char.IsWhiteSpace(tmp))
{
newarr[j] = tmp;
++j;
}
}
return new String(newarr, 0, j);
}
Регулярное выражение избыточное; просто используйте расширение на строке (спасибо Henk). Это тривиально и должно быть частью структуры. Во всяком случае, здесь моя реализация:
public static partial class Extension
{
public static string RemoveWhiteSpace(this string self)
{
return new string(self.Where(c => !Char.IsWhiteSpace(c)).ToArray());
}
}
Вот простая линейная альтернатива решению RegEx. Я не уверен, что быстрее; вам придется сравнивать его.
static string RemoveWhitespace(string input)
{
StringBuilder output = new StringBuilder(input.Length);
for (int index = 0; index < input.Length; index++)
{
if (!Char.IsWhiteSpace(input, index))
{
output.Append(input[index]);
}
}
return output.ToString();
}
Мне нужно было заменить пробел в строке пробелами, но не дублировать пробелы. например, мне нужно было преобразовать что-то вроде следующего:
"a b c\r\n d\t\t\t e"
к
"a b c d e"
Я использовал следующий метод
private static string RemoveWhiteSpace(string value)
{
if (value == null) { return null; }
var sb = new StringBuilder();
var lastCharWs = false;
foreach (var c in value)
{
if (char.IsWhiteSpace(c))
{
if (lastCharWs) { continue; }
sb.Append(' ');
lastCharWs = true;
}
else
{
sb.Append(c);
lastCharWs = false;
}
}
return sb.ToString();
}
Я предполагаю, что ваш ответ XML выглядит следующим образом:
var xml = @"<names>
<name>
foo
</name>
<name>
bar
</name>
</names>";
Лучший способ обработки XML - использовать синтаксический анализатор XML, например LINQ to XML:
var doc = XDocument.Parse(xml);
var containsFoo = doc.Root
.Elements("name")
.Any(e => ((string)e).Trim() == "foo");
Вот еще один вариант:
public static string RemoveAllWhitespace(string aString)
{
return String.Join(String.Empty, aString.Where(aChar => aChar !Char.IsWhiteSpace(aChar)));
}
Как и в большинстве других решений, я не выполнял исчерпывающие тесты тестов, но это работает достаточно хорошо для моих целей.
Я нашел разные результаты, чтобы быть правдой. Я пытаюсь заменить все пробелы на одно пространство, и регулярное выражение было очень медленным.
return( Regex::Replace( text, L"\s+", L" " ) );
Что наиболее оптимально работало для меня (в С++ cli):
String^ ReduceWhitespace( String^ text )
{
String^ newText;
bool inWhitespace = false;
Int32 posStart = 0;
Int32 pos = 0;
for( pos = 0; pos < text->Length; ++pos )
{
wchar_t cc = text[pos];
if( Char::IsWhiteSpace( cc ) )
{
if( !inWhitespace )
{
if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );
inWhitespace = true;
newText += L' ';
}
posStart = pos + 1;
}
else
{
if( inWhitespace )
{
inWhitespace = false;
posStart = pos;
}
}
}
if( pos > posStart ) newText += text->Substring( posStart, pos - posStart );
return( newText );
}
Сначала я попытался выполнить описанную выше процедуру, заменив каждый символ отдельно, но мне пришлось переключиться на выполнение подстрок для не-пространственных разделов. При применении к строке в 1 200 000 символов:
Мы можем использовать:
public static string RemoveWhitespace(this string input)
{
if (input == null)
return null;
return new string(input.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.ToArray());
}
Скажем, у нас есть эта строка: string MyString = " test test test"
.
Вы можете разделить строку с одним разделителем пробелов: MyString.Split(' ')
.
В результате получается массив пустых строк и других чартеров.
В этом случае он генерирует этот массив: { "", "test", "test", "", "", "", "test" }
.
Затем, используя метод string.concat, вы можете объединить все строки в массиве, что приводит к "testtesttest"
.
string MyString = " test test test";
MyString = string.Concat(MyString.Split(' '));
Мы можем использовать System.Linq, и мы можем сделать это в одной строке:
string text = "My text with white spaces...";
text = new string(text.ToList().Where(c => c != ' ').ToArray());
String s = Console.ReadLine();
s = s.Replace(" ", String.Empty);
String[] arr = s.Split(' ');
foreach(char num in s)
{
Console.WriteLine(num);
}
Этот блок кода удаляет все пробелы из строки.