Мой ввод состоит из строки неопределенной длины и содержания. Мне нужно обрезать все пробелы, символы #\t и фрагменты ///**/ в его начале и конце. Все эти символы и наборы символов следуют друг за другом. Если между ними было что-то еще, нужно остановиться.
Пример:
"///g, hhh/ , test" ===> "/g, hhh/ , test"
"//*g, hhh/ , test " ===> "*g, hhh/ , test"
"#/test" ===> "/test"
"hello //" ===> "hello"
Важное уточнение: мне нужно знать, сколько символов я обрезал в начале и сколько символов я обрезал в конце.
Кроме того, я бы не хотел использовать регулярные выражения, потому что этот код очень чувствителен к производительности. Насколько я знаю, регулярные выражения довольно медленные. Однако, если моя задача не может быть выполнена с помощью циклов или что-то подобное чрезвычайно сложно — я готов использовать регулярные выражения.
До сих пор я пробовал такой код. Задача усложняется тем, что некоторые символы представляют собой два следующих друг за другом символа.
private const char specialSymbol_1 = '/';
private const char specialSymbol_2 = '*';
private const char specialSymbol_3 = '#';
private void ObserveTrim(ref string target, ref int start, ref int end) {
int s = 0; int e = 0;
for (int i = 0; i < target.Length; ++i) {
char c = target[i];
bool flag = false;
if (target.Length > 1) {
if (i == 0) {
flag = c == specialSymbol_1 && target[i + 1] == specialSymbol_1 ||
c == specialSymbol_1 && target[i + 1] == specialSymbol_2 ||
c == specialSymbol_2 && target[i + 1] == specialSymbol_1;
}
else if (i == target.Length - 1) {
flag = c == specialSymbol_1 && target[i - 1] == specialSymbol_1 ||
c == specialSymbol_1 && target[i - 1] == specialSymbol_2 ||
c == specialSymbol_2 && target[i - 1] == specialSymbol_1;
}
else {
flag = c == specialSymbol_1 && target[i + 1] == specialSymbol_1 ||
c == specialSymbol_1 && target[i - 1] == specialSymbol_1 ||
c == specialSymbol_1 && target[i + 1] == specialSymbol_2 ||
c == specialSymbol_1 && target[i - 1] == specialSymbol_2;
}
}
if (flag) continue;
if (!char.IsWhiteSpace(c) && c != specialSymbol_3) {
s = i;
break;
}
}
for (int i = target.Length - 1; i >= 0; --i) {
char c = target[i];
bool flag = false;
if (target.Length > 1) {
if (i == 0) {
flag = c == specialSymbol_1 && target[i + 1] == specialSymbol_1 ||
c == specialSymbol_1 && target[i + 1] == specialSymbol_2 ||
c == specialSymbol_2 && target[i + 1] == specialSymbol_1;
}
else if (i == target.Length - 1) {
flag = c == specialSymbol_1 && target[i - 1] == specialSymbol_1 ||
c == specialSymbol_1 && target[i - 1] == specialSymbol_2 ||
c == specialSymbol_2 && target[i - 1] == specialSymbol_1;
}
else {
flag = c == specialSymbol_1 && target[i + 1] == specialSymbol_1 ||
c == specialSymbol_1 && target[i - 1] == specialSymbol_1 ||
c == specialSymbol_1 && target[i + 1] == specialSymbol_2 ||
c == specialSymbol_1 && target[i - 1] == specialSymbol_2;
}
}
if (flag) continue;
if (!char.IsWhiteSpace(c) && c != specialSymbol_3) {
e = target.Length - 1 - i;
break;
}
}
start += s;
end -= e;
target = target.Substring(s, target.Length - s - e);
}
Но этот код не работает должным образом.
Пример:
"///g, hhh/ , test" ===> "g, hhh/ , test""/*g, hhh/ , test" ===> "*g, hhh/ , test"
Это лишь несколько примеров его некорректной работы, на самом деле их несколько десятков.
Он просто не учитывает некоторые символы или их последовательность. Я не силен в таких алгоритмах, и любая помощь приветствуется.





Имея все строки для trim, я предлагаю проверить их все с помощью StartsWith и EndsWith. С помощью ReadOnlySpan<char> мы можем сделать это, не создавая много нежелательных подстрок.
Так как вы хотите получить параметры 3 - result (обрезанная строка), сколько символов было удалено из left и из right, давайте объединим их в кортеж:
Код:
public static (string result, int left, int right) MyTrim(
string value, params string[] trim) {
if (string.IsNullOrEmpty(value) || trim is null || trim.Length == 0)
return (value, 0, 0);
int trimmedLeft = 0;
int trimmedRight = 0;
var span = value.AsSpan();
for (bool keep = true; keep; ) {
keep = false;
foreach (var item in trim)
if (!string.IsNullOrEmpty(item) && span.StartsWith(item)) {
trimmedLeft += item.Length;
span = span.Slice(item.Length);
keep = true;
break;
}
}
for (bool keep = true; keep; ) {
keep = false;
foreach (var item in trim)
if (!string.IsNullOrEmpty(item) && span.EndsWith(item)) {
trimmedRight += item.Length;
span = span.Slice(0, span.Length - item.Length);
keep = true;
break;
}
}
return (span.ToString(), trimmedLeft, trimmedRight);
}
Использование:
string value = "///g, hhh/ , test";
(string result, int left, int right) = MyTrim(
value, " ", "#", "\t", "//", "/*", "*/");
Демо:
using System.Linq;
...
string[] tests = new string[] {
"///g, hhh/ , test",
"//*g, hhh/ , test ",
"#/test",
"hello //",
};
var report = string.Join(Environment.NewLine, tests
.Select(test => (test, result : MyTrim(test, " ", "#", "\t", "//", "/*", "*/")))
.Select(pair => $"{pair.test,30} ===> {pair.result.result} (left: {pair.result.left}; right: {pair.result.right}) "));
Console.WriteLine(report);
Выход:
///g, hhh/ , test ===> /g, hhh/ , test (left: 2; right: 0)
//*g, hhh/ , test ===> *g, hhh/ , test (left: 2; right: 1)
#/test ===> /test (left: 1; right: 0)
hello // ===> hello (left: 0; right: 4)
Спасибо за ответ и подробное объяснение. К сожалению, я вынужден использовать специальную версию C# (я не знаю, какую именно, так как у меня есть доступ только для написания скрипта, который компилируется сторонней программой, а затем используется в ней). Я не могу компилировать с использованием кортежей, и некоторые классы, такие как Tuple. Однако на данный момент я модифицирую этот код под свои нужды, и в конце дам вам знать, что из этого получится.
У меня нет возможности использовать Span. Существуют ли какие-либо эквиваленты для более ранних версий C#? Я предполагаю, что у меня 3-4 версии (с некоторыми ограничениями). Я также не могу установить пакеты NuGet
@Unable error: вы можете использовать value.Substring(...) вместо span.Slice(...), но это замедлит работу.
@Unable error: dotnetfiddle.net/LJG9bi, MyTrimOld метод: нет Tuples, нет Spans
Этот ответ по сути является решением. Для тех, кто сталкивается с теми же ограничениями, что и я, я разместил свой ответ: stackoverflow.com/a/76212877/16029325
Несмотря на то, что ответ Дмитрия был исчерпывающим, я не смог применить его в исходном виде, т.к. сильно ограничен компилятором.
Я писал скрипт для программы и даже не знаю, какую языковую версию она поддерживает. В связи с этим я переписал код и использовал только те конструкции, которые удалось скомпилировать. Публикую этот даунгрейд для тех, кто оказался в моих условиях. Жаль, что пришлось пожертвовать удобством и производительностью. Однако я отмечу его ответ как решение, так как большинство компиляторов будут обрабатывать этот код в наше время.
Я также частично отредактировал вопрос, чтобы проблема и ее решение выглядели более наглядными для тех, кто сталкивается с похожей проблемой.
private const char specialSymbol_1 = '/';
private const char specialSymbol_2 = '*';
private const char specialSymbol_3 = '#';
private void CheckContent() {
int leftWordStart = ...;
int leftWordEnd = ...;
string leftWord = ...;
MyTrim(ref leftWord, ref leftWordStart, ref leftWordEnd,
new string[6] { " ", "\t", new string(specialSymbol_1, 2), new string(new char[2] {specialSymbol_1, specialSymbol_2}),
new string(new char[2] {specialSymbol_2, specialSymbol_1}), specialSymbol_3.ToString()});
//...
}
private void MyTrim(ref string target, ref int start, ref int end, string[] trim) {
if (string.IsNullOrWhiteSpace(target) || trim == null || trim.Length == 0) return;
int trimmedLeft = 0;
int trimmedRight = 0;
bool keep = true;
while (keep) {
keep = false;
for (int i = 0; i < trim.Length; ++i) {
string item = trim[i];
if (!string.IsNullOrEmpty(item) && target.StartsWith(item)) {
trimmedLeft += item.Length;
target = target.Substring(item.Length);
keep = true;
break;
}
}
}
keep = true;
while (keep) {
keep = false;
for (int i = 0; i < trim.Length; ++i) {
string item = trim[i];
if (!string.IsNullOrEmpty(item) && target.EndsWith(item)) {
trimmedRight += item.Length;
target = target.Substring(0, target.Length - item.Length);
keep = true;
break;
}
}
}
start += trimmedLeft;
end -= trimmedRight;
}
Если «этот код не работает должным образом», вы можете указать, каковы были ваши ожидания и что вместо этого сделал ваш код (что ваш код делает неправильно).