Мы находимся в обработке интеграции связи с внешним API. До сих пор это была небольшая головная боль из-за несогласованного наименования, плохой документации и ненадежных сообщений об ошибках/ошибках.Почему Regex и StringBuilder замедляются при удалении пробела?
Одна из вещей, с которыми мы имеем дело, состоит в том, что определенные Запросы, которые мы отправляем им, имеют ограничения на длину строк. Ничего революционного, но любой запрос, который содержит любую строку, которая превышает требования к длине, просто отрицается и терпит неудачу.
Наше решение было создать метод расширения для строки, которая только принимает в максимальной длины и возвращает подстроку той длины, начиная с индекса 0.
Я младший DEV и это моя первая работа , поэтому я знаю, что мои решения, возможно, не самые элегантные или эффективные. В любом случае, я поднял вопрос, что с нашим текущим расширением мы могли бы завершить удаление соответствующей информации, включая потенциально бесполезное пустое пространство, так как мы не обрезали или ничего не делали, чтобы проверять наличие двойных пробелов и т. Д. Мое руководство помогло мне сделать перегрузка расширения, которое позволит вам также исключить пустое пространство.
У меня есть 3 решения, которые полностью удаляют любые двойные пробелы. Я знаю, что метод Regex является единственным, который действительно удаляет все пустое пространство, где, как и другие два, удаляет любое появление двух пространств назад. Однако этот сайт будет использоваться исключительно в США, поэтому я не уверен, что дополнительное время для Regex оправдано.
Мой главный интерес в публикации этого вопроса Я задаюсь вопросом, может ли кто-нибудь объяснить, почему мой метод с использованием StringBuilder настолько неэффективен по сравнению с двумя другими, он даже медленнее, чем Regex, я ожидал, что он будет самым быстрым из трех. Любое понимание здесь ценится, а также намек на то, что может быть лучшим способом, чем любой из них, который я придумал.
Вот мои три расширения:
public static string SafeSubstringSomehowTheQuickest(this string stringToShorten, int maxLength)
{
if (stringToShorten?.Length < maxLength || string.IsNullOrWhiteSpace(stringToShorten)) return stringToShorten;
stringToShorten = stringToShorten.Trim();
int stringOriginalLength = stringToShorten.Length;
int extraWhitespaceCount = 0;
for (int i = 0; i < stringOriginalLength - extraWhitespaceCount; i++)
{
int stringLengthBeforeReplace = stringToShorten.Length;
stringToShorten = stringToShorten.Replace(" ", " ");
if(stringLengthBeforeReplace < stringToShorten.Length) { extraWhitespaceCount += stringToShorten.Length - stringLengthBeforeReplace; }
}
return stringToShorten.Length > maxLength ? stringToShorten.Substring(0, maxLength) : stringToShorten;
}
public static string SafeSubstringWithRegex(this string stringToShorten, int maxLength)
{
if (stringToShorten?.Length < maxLength || string.IsNullOrWhiteSpace(stringToShorten)) return stringToShorten;
stringToShorten = System.Text.RegularExpressions.Regex.Replace(stringToShorten, @"\s{2,}", " ").Trim();
return stringToShorten.Length > maxLength ? stringToShorten.Substring(0, maxLength) : stringToShorten;
}
public static string SafeSubstringFromBuilder(this string stringToShorten, int maxLength)
{
if (stringToShorten?.Length < maxLength || string.IsNullOrWhiteSpace(stringToShorten)) return stringToShorten;
StringBuilder bob = new StringBuilder();
bool lastCharWasWhitespace = false;
foreach (char c in stringToShorten)
{
if (c == ' ' && !lastCharWasWhitespace) { bob.Append(c); }
lastCharWasWhitespace = c == ' ';
if (!lastCharWasWhitespace) { bob.Append(c); }
}
stringToShorten = bob.ToString().Trim();
return stringToShorten.Length < maxLength ? stringToShorten : stringToShorten.Substring(0, maxLength);
}
Вот мой быстрый тест, я использую, чтобы сравнить время, необходимое для каждого расширения для запуска:
static void Main(string[] args)
{
var stopwatch = new System.Diagnostics.Stopwatch();
string test =
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar " +
" foo bar foobar f oo bar foobar foofoo " +
"barbar foo b ar ";
int stringStartingLength = test.Length;
int stringMaxLength = 30;
stopwatch.Start();
string somehowTheQuickestResult = test.SafeSubstringSomehowTheQuickest(stringMaxLength);
stopwatch.Stop();
var somehowTheQuickestResultTicks = stopwatch.ElapsedTicks;
stopwatch.Start();
string regexResult = test.SafeSubstringWithRegex(stringMaxLength);
stopwatch.Stop();
var regexResultTicks = stopwatch.ElapsedTicks;
stopwatch.Start();
string stringBuilderResult = test.SafeSubstringFromBuilder(stringMaxLength);
stopwatch.Stop();
var stringBuilderResultTicks = stopwatch.ElapsedTicks;
}
Наконец эти результаты , тики различаются по каждому прогону, но разница между тремя методами довольно непротиворечива:
Все три возвращают одну и ту же строку из: "Foo бар Foobar е оо бар Foobar"
somehowTheQuickestResult (метод 1): 12840 клещи
regexResult (метод 2): 14889 клещей
stringBuilderResult (метод 3): 15798 тиков
Возможно, было бы еще быстрее, если бы вы работали с массивом символов и перемещали небелые пробелы. Для больших сравнений строк вы, вероятно, получите совсем другие результаты с вашими текущими методами. – BugFinder
В качестве примечания стороны методы возвратят строку из множества пространств без изменений, хотя она будет слишком длинной. – GSerg
Для регулярного выражения вы, вероятно, хотите исключить время компиляции, создав явный экземпляр «static readonly» из него с обязательными [flags] (https://msdn.microsoft.com/en-us/library/h5845fdz (v = vs 0,110) .aspx). Для stringbuilder вы, вероятно, хотите передать 'maxLength' как [' capacity'] (https://msdn.microsoft.com/en-us/library/h1h0a5sy (v = vs.110) .aspx). – GSerg