6

Нужно сделать регулярное выражение, которое будет удалять предыдущий символ перед текстом <backspace>. Казалось бы, это реализуется очень просто:

str = Regex.Replace(str, @".<backspace>", "");

Но тут есть подводные камни. Если, например, в тексте идет 2 <backspace> подряд, тогда конструкция рушится. Можно, конечно, сделать такое решение:

str = Regex.Replace(str, @".<backspace>|..<backspace><backspace>", "");

но это бред, ведь может быть и 10 <backspace> подряд и надо будет удалить 10 предыдущих символов. Как сделать универсальным это регулярное выражение?


Может быть такая строка:

string str = "это буу<backspace>дка<backspace><backspace>ет тек<backspace>стовыыф<backspace><backspace>й ткаукц<backspace><backspace><backspace><backspace><backspace>екст";

В итоге должны получить:

"это будет тестовый текст"
Max
  • 466
  • 5
  • 14
  • 1
    str = Regex.Replace(str, "(?(bs)(?!))((?<bs><backspace>)|(?<-bs>.(?<!<backspace>)))+?", string.Empty, RegexOptions.RightToLeft); – user181245 Aug 20 '17 at 17:34

2 Answers2

9

В .NET можно решить эту проблему регулярным выражением с проверкой состояния стека захватывающих групп:

^(?:<backspace>)+|(?:(?<t>(?<!<backspace).)|(?<-t><backspace>))+(?(t)(?!))(?:<backspace>)*

См. демо регулярного выражения.

Подробности

  • ^(?:<backspace>)+ - один и более последовательностей символов <backspace> в начале строки (^)
  • | - или
  • (?:(?<t>(?<!<backspace).)|(?<-t><backspace>))+ - незахватывающая группа ((?:...)), находящая одну или более (+ — жадный квантификатор, находит 1 и более последовательных совпадений) последовательностей (?<t>(?<!<backspace).) или (?<-t><backspace>):
    • (?<t>(?<!<backspace).) - любой символ, перед которым нет подстроки <backspace (так как использован флаг RegexOptions.Singleline, точка также находит символ перевода на новую строку) (при этом каждое совпадение будет добавлено в стек группы t)
    • (?<-t><backspace>) - одна и более последовательностей символов <backspace> (при этом каждый раз при найденном совпадении из стека группы t будет удаляться последнее значение)
  • (?(t)(?!)) - условная конструкция: если стек группы t не пуст, совпадение считается неверным, и запускается поиск с возвратом (backtracking), иначе возвращается совпадение (такое, где количество значений, найденных (?<t>.) равно количеству значений, найденных (?<-t><backspace>))
  • (?:<backspace>)* - ноль и более последовательностей символов <backspace>

Можно объявить это выражение в коде следующим образом:

public static class Rx
{
    public static readonly Regex backspaceRx = new Regex(
        @"^(?:<backspace>)+          # 1+ <backspace> в начале
           |                         # или
           (?:
             (?<t>(?<!<backspace).)  # Любой символ, перед которым нет <backspace 
             |                       # или
             (?<-t><backspace>)      # <backspace>
           )+                        # 1 или более раз
           (?(t)(?!))                # проверка стека группы t
           (?:<backspace>)*          # 0+ <backspace>",
    RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
}

Тест на C#:

var tests = new List<string> {"<backspace>это буу<backspace>дка<backspace><backspace>ет тек<backspace>стовыыф<backspace><backspace>й ткаукц<backspace><backspace><backspace><backspace><backspace>екст",
        "Это<backspace><backspace><backspace><backspace><backspace><backspace>",
        "a<backspace><backspace><backspace>b",
        "<backspace><backspace><backspace><backspace>",
        "ab<backspace>c<backspace><backspace>d" };
    foreach(var test in tests)
        Console.WriteLine("'{0}'", Rx.backspaceRx.Replace(test, string.Empty));

Вывод:

'это будет тестовый текст'
''
'b'
''
'd'
  • 1
    Ничего себе! А где можно об этом почитать? (Если есть хорошая книга, может быть, добавите её в список литературы?) – VladD Aug 20 '17 at 20:43
  • 1
    Всё, что я знаю о регулярных выражения, почерпнуто из англоязычных источников. О стеке, условных шаблонах, балансировочных группах можно почитать здесь на русском. – Wiktor Stribiżew Aug 20 '17 at 20:59
  • 1
    Жаль, что нету русскоязычных книг. А можно ссылку на англоязычный оригинал вопроса? Очень уж машинный перевод глаз режет. // Ага, нашёл: What are regular expression Balancing Groups?. Пусть полежит здесь для будущих читателей. – VladD Aug 20 '17 at 21:19
  • Попробуйте string str = "a<backspace><backspace><backspace>b"; в качестве входных данных. Или string str = "ab<backspace>c<backspace><backspace>d";. – user181245 Aug 20 '17 at 21:43
  • @PetSerAl: http://ideone.com/DfMHKb. Нужно отсеять ненужные совпадения, уточнив . с помощью предварительного блока просмотра назад и удалить непарные <backspace>. – Wiktor Stribiżew Aug 20 '17 at 21:49
  • string str = "<backspacex<backspace>>"; – user181245 Aug 20 '17 at 21:56
  • @PetSerAl: А это уже не понятно, входит ли в условие. Если так, то уже и a<bX<backspace>ackspace>. Думаю, это не будет валидным текстом в грамматике вопроса. – VladD Aug 20 '17 at 22:11
  • Вообще-то, можно обойтись только регулярным выражением. Надо отловить все <backspace> после проверки стека. Да, вряд ли это валидно. – Wiktor Stribiżew Aug 20 '17 at 22:26
  • Ничего себе, огромное спасибо! – Max Aug 21 '17 at 14:31
  • @WiktorStribiżew И всё-таки для ввода ab<backspace>c<backspace><backspace>d ваш код возвращает ad, вместо d. – user181245 Aug 21 '17 at 16:48
  • @PetSerAl Если есть ещё примеры, давайте. А решение я обновил. – Wiktor Stribiżew Aug 21 '17 at 22:18
1

Думаю, одной изящной регуляркой это не сделать.

Я придумал следующее:

Match m;
do
{
    m = Regex.Match(str, ".<backspace>");
    str = str.Remove(m.Index, m.Length);
} while (m.Success);

В цикле находим и удаляем по одному вхождению паттерна. Конечно, этот способ не блещет производительностью.