C# Обрезка с обеих сторон строки всех необходимых символов и наборов символов по порядку

Мой ввод состоит из строки неопределенной длины и содержания. Мне нужно обрезать все пробелы, символы #\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"

Это лишь несколько примеров его некорректной работы, на самом деле их несколько десятков.

Он просто не учитывает некоторые символы или их последовательность. Я не силен в таких алгоритмах, и любая помощь приветствуется.

Если «этот код не работает должным образом», вы можете указать, каковы были ваши ожидания и что вместо этого сделал ваш код (что ваш код делает неправильно).

Scott Hunter 09.05.2023 19:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Имея все строки для 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. Однако на данный момент я модифицирую этот код под свои нужды, и в конце дам вам знать, что из этого получится.

Unable error 09.05.2023 20:45

У меня нет возможности использовать Span. Существуют ли какие-либо эквиваленты для более ранних версий C#? Я предполагаю, что у меня 3-4 версии (с некоторыми ограничениями). Я также не могу установить пакеты NuGet

Unable error 09.05.2023 21:13

@Unable error: вы можете использовать value.Substring(...) вместо span.Slice(...), но это замедлит работу.

Dmitry Bychenko 09.05.2023 21:15

@Unable error: dotnetfiddle.net/LJG9bi, MyTrimOld метод: нет Tuples, нет Spans

Dmitry Bychenko 09.05.2023 21:21

Этот ответ по сути является решением. Для тех, кто сталкивается с теми же ограничениями, что и я, я разместил свой ответ: stackoverflow.com/a/76212877/16029325

Unable error 09.05.2023 22:00

Несмотря на то, что ответ Дмитрия был исчерпывающим, я не смог применить его в исходном виде, т.к. сильно ограничен компилятором.

Я писал скрипт для программы и даже не знаю, какую языковую версию она поддерживает. В связи с этим я переписал код и использовал только те конструкции, которые удалось скомпилировать. Публикую этот даунгрейд для тех, кто оказался в моих условиях. Жаль, что пришлось пожертвовать удобством и производительностью. Однако я отмечу его ответ как решение, так как большинство компиляторов будут обрабатывать этот код в наше время.

Я также частично отредактировал вопрос, чтобы проблема и ее решение выглядели более наглядными для тех, кто сталкивается с похожей проблемой.

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;
}

Другие вопросы по теме