В С# я хочу обрезать() оба конца строки всех возможных типов пробельных символов (как определено .IsWhitespace), а также обрезать начало строки, чтобы ТАКЖЕ удалить любой символ из пользовательского списка в char[] массив, встречающийся перед обычными буквенно-цифровыми символами. Таким образом, строка должна заканчиваться так, чтобы первый символ не был пробелом и не был одним из символов в моем пользовательском списке. (Тем не менее, строка может, конечно, включать пробелы или символы в моем списке ПОСЛЕ первого символа.)
Я хочу выполнить итерацию по строке только один раз, чтобы сделать все это, потому что есть большой список строк, которые нужно обрезать.
Следующее не работает (см. дополнительные комментарии ниже фрагмента кода).
static class TrimmerExtension
{
private const string _prefixCharsToTrimAsString = "~`$|-_";
private static readonly char[] _prefixCharsToTrim;
// static ctor
static TrimmerExtension()
{
_prefixCharsToTrim = _prefixCharsToTrimAsString.ToCharArray();
}
public static string TrimWhitespaceAndPrefixes(this string s)
{
return s.Trim().TrimStart(_prefixCharsToTrim).TrimStart();
}
}
Вышеупомянутое не работает, потому что:
он вызывает разные версии Trim несколько раз, таким образом, несколько раз перебирая строку. (Поэтому вышеизложенное неэффективно);
он не полностью обрезает все пробелы и специальные символы с самого начала, если между другими символами есть пробелы, которые нужно обрезать; например, если строка содержит «$ ~ Something», метод расширения вернет «~ Something», но первые два символа все равно нужно удалить. (Таким образом, приведенный выше код неверен.)
Предоставляют ли C# или .NET эффективный способ сделать это (с точки зрения как производительности, так и простоты исходного кода)? В качестве альтернативы, существует ли класс или библиотека, которая определяет постоянный массив символов (или строк) всех возможных пробельных символов, которые функция Trim() удалит из строки, поэтому я могу добавить его в список моих специальных символов префикса, которые будут удаленный?
Если на этот конкретный вопрос уже дан ответ, опубликуйте ссылку на вопрос и ответ(а).
Спасибо.
Эффективность на самом деле не проблема, если вы вызываете Trim один раз, обрезав все символы, которые вам нужны. Если вам нужно что-то без распределения, потому что усеченное значение находится в стадии разработки, используйте Spans.
@Hans Kesting: строка поставляется из внешнего источника, над которым я не властен. Поэтому, хотя я обычно не ожидаю, что он будет содержать пробельные символы, кроме, скажем, TAB или SPACE, я не могу этого гарантировать.
Вы можете написать небольшую программу, которая перебирает все значения char и печатает коды всех пробелов. Будем надеяться, что нет суррогатных пар, которые являются пробелами
@madreflection «все символы, которые нужно обрезать». Правильно, но это вопрос. Как мне включить все пробельные символы, которые Trim обычно удаляет? Я не могу нигде найти окончательного списка; то есть либо массив char[], либо определение строки, включая все пробелы.





Почему бы не сделать это своими руками?
public static string MySpecialTrim(string str, char[] trimChars)
{
for (var i = 0; i < str.Length; ++i)
{
char ch = str[i];
if (!char.IsWhiteSpace(ch) && !trimChars.Contains(ch))
return str.Substring(i);
}
return str;
}
Таким образом, вы контролируете, какие символы обрезаются.
TrimStart().string input = "$%Hello World";
char[] trimChars = {' ', '$', '%'};
string output = input.TrimStart(trimChars);
Console.WriteLine(output); // Output: "Hello World"
Чтобы удалить из конца строки, вы можете использовать TrimEnd().
Для получения дополнительной информации о TrimStart и TrimEnd.
Да, они знают, что могут использовать TrimStart, но не могут, как объясняется в их вопросе под кодом.
Я так понимаю: char[] trimChars = {' ', '$', '%'}; строковый вывод = input.TrimStart (trimChars); удалит только указанные символы, и единственный символ пробела, который будет удален, - это просто сам пробел. Я считаю, что Trim() удаляет целое множество различных типов символов пробела, и я хотел бы, чтобы они ВСЕ были удалены от начала строки.
Похоже, простого пути нет, так как string.Trim() использует для этого два разных метода.
TrimWhiteSpaceHelper для пробелов:
private string TrimWhiteSpaceHelper(TrimType trimType)
{
// end will point to the first non-trimmed character on the right.
// start will point to the first non-trimmed character on the left.
int end = Length - 1;
int start = 0;
// Trim specified characters.
if ((trimType & TrimType.Head) != 0)
{
for (start = 0; start < Length; start++)
{
if (!char.IsWhiteSpace(this[start]))
{
break;
}
}
}
if ((trimType & TrimType.Tail) != 0)
{
for (end = Length - 1; end >= start; end--)
{
if (!char.IsWhiteSpace(this[end]))
{
break;
}
}
}
return CreateTrimmedString(start, end);
}
и TrimHelper для пользовательских символов:
private unsafe string TrimHelper(char* trimChars, int trimCharsLength, TrimType trimType)
{
Debug.Assert(trimChars != null);
Debug.Assert(trimCharsLength > 0);
// end will point to the first non-trimmed character on the right.
// start will point to the first non-trimmed character on the left.
int end = Length - 1;
int start = 0;
// Trim specified characters.
if ((trimType & TrimType.Head) != 0)
{
for (start = 0; start < Length; start++)
{
int i = 0;
char ch = this[start];
for (i = 0; i < trimCharsLength; i++)
{
if (trimChars[i] == ch)
{
break;
}
}
if (i == trimCharsLength)
{
// The character is not in trimChars, so stop trimming.
break;
}
}
}
if ((trimType & TrimType.Tail) != 0)
{
for (end = Length - 1; end >= start; end--)
{
int i = 0;
char ch = this[end];
for (i = 0; i < trimCharsLength; i++)
{
if (trimChars[i] == ch)
{
break;
}
}
if (i == trimCharsLength)
{
// The character is not in trimChars, so stop trimming.
break;
}
}
}
return CreateTrimmedString(start, end);
}
Их объединение может выглядеть так:
public static class TrimmerExtension
{
public static string TrimWhitespaceAndPrefixes(this string str, params char[] trimChars)
{
var trimCharsLength = trimChars.Length;
int start;
for (start = 0; start < str.Length; start++)
{
var ch = str[start];
if (!char.IsWhiteSpace(ch))
{
int i;
for (i = 0; i < trimCharsLength; i++)
{
if (trimChars[i] == ch)
{
break;
}
}
if (i == trimCharsLength)
{
break;
}
}
}
return str[start..];
}
}
Если производительность не очень важна, я предлагаю использовать регулярные выражения или сокращенную версию из https://stackoverflow.com/a/76133168/2770274
Спасибо, @Adassko. Я думаю, мне нужно написать собственный метод для итерации по строке, и похоже, что этот ответ (или что-то подобное) - это путь. Как я указал в своем вопросе, производительность МОЖЕТ быть проблемой, потому что исходный список строк для обработки может стать довольно большим.
в этом случае я предлагаю также вернуть ReadOnlySpan<> вместо строки, это может дать большой прирост производительности, если вы продолжите обработку этих строк.
Я никогда раньше не слышал о Span<>, поэтому никогда им не пользовался. Я посмотрю на это. Спасибо!.
Вы ожидаете больше пробелов, чем CR, LF, TAB, SPACE? Почему бы не добавить их явно в список обрезки?