Как найти и заменить конкретный символ, но только если он находится в кавычках?

Проблема. У меня есть тысячи документов, которые содержат определенный символ, которого я не хочу. Например, символ a. Эти документы содержат множество символов, но я хочу заменить внутри двойных или одинарных кавычек. a

Я хотел бы найти и заменить их, и я думал, что использование Regex будет необходимо. Я использую VSCode, но я открыт для любых предложений.

Моя попытка: я смог найти следующее регулярное выражение для соответствия для определенной строки, содержащей значения внутри ().

".*?(r).*?"

Однако это только подчеркивает всю цитату. Я хочу выделить только персонажа.

Любое решение, возможно, вне регулярного выражения, приветствуется.

Пример результата: Учитывая, характер, найти замену для a b

Somebody once told me "apples" are good for you => Somebody once told me "bpples" are good for you

"Aardvarks" make good kebabs => "Abrdvbrks" make good kebabs

The boy said "aaah!" when his mom told him he was eating aardvark The boy said "aaah!" when his mom told him he was eating aardvark => The boy said "bbbh!" when his mom told him he was eating aardvark The boy said "bbbh!" when his mom told him he was eating aardvark

Ответы

Ответ 1

Код Visual Studio

В VS Code используется механизм JavaScript RegEx для его поиска/замены. Это означает, что вы очень ограничены в работе с регулярным выражением по сравнению с другими вариантами, такими как.NET или PCRE.

К счастью, этот аромат поддерживает взгляды и взгляды, которые вы можете искать, но не потреблять характер. Таким образом, один из способов гарантировать, что мы находимся в кавычной строке, - это искать число котировок вниз до строки файла/субъекта, чтобы быть нечетным после сопоставления a:

a(?=[^"]*"[^"]*(?:"[^"]*"[^"]*)*$)

Демо-версия

Это ищет a в строке с двойными кавычками, чтобы она для одиночных кавычек заменяла все " с '. Вы не можете иметь оба за раз.

Однако существует проблема с регулярным выражением выше, однако он конфликтует с экранированными двойными кавычками в двойных кавычках. Чтобы соответствовать им, если это имеет значение, вам предстоит пройти долгий путь:

a(?=[^"\\]*(?:\\.[^"\\]*)*"[^"\\]*(?:\\.[^"\\]*)*(?:"[^"\\]*(?:\\.[^"\\]*)*"[^"\\]*(?:\\.[^"\\]*)*)*$)

Применение этих подходов к большим файлам, вероятно, приведет к переполнению стека, поэтому давайте посмотрим на лучший подход.

Я использую VSCode, но я открыт для любых предложений.

Это здорово. Тогда я бы предложил использовать awk или sed или что-то более программное, чтобы добиться того, что вам нужно, или если вы можете использовать Sublime Text, существует вероятность, чтобы более эффективно использовать эту проблему.

Возвышенный текст

Предполагается, что он работает с большими файлами со сто тысяч строк, но заботится о том, чтобы он работал для одного символа (здесь a), что с некоторыми изменениями может работать и для слова или подстроки:

Ищи:

(?:"|\G(?<!")(?!\A))(?<r>[^a"\\]*+(?>\\.[^a"\\]*)*+)\K(a|"(*SKIP)(*F))(?(?=((?&r)"))\3)
                           ^              ^            ^

Замените его: WHATEVER\3

Демо-версия

Регрессионный анализ:

(?: # Beginning of non-capturing group #1
    "   # Match a '"'
    |   # Or
    \G(?<!")(?!\A)  # Continue matching from last successful match
                    # It shouldn't start right after a '"'
)   # End of NCG #1
(?<r>   # Start of capturing group 'r'
    [^a"\\]*+   # Match anything except 'a', '"' or a backslash (possessively)
    (?>\\.[^a"\\]*)*+   # Match an escaped character or 
                        # repeat last pattern as much as possible
)\K     # End of CG 'r', reset all consumed characters
(   # Start of CG #2 
    a   # Match literal 'a'
    |   # Or
    "(*SKIP)(*F)    # Match a '"' and skip over current match
)
(?(?=   # Start a conditional cluster, assuming a positive lookahead
    ((?&r)")    # Start of CG #3, recurs CG 'r' and match '"'
  )     # End of condition
  \3    # If conditional passed match CG #3
 )  # End of conditional

enter image description here

Трехэтапный подход

Последний, но тем не менее важный...

Совпадение символа внутри кавычек сложно, поскольку разделители точно такие же, что и открывающие и закрывающие метки нельзя отличить друг от друга, не обращая внимания на соседние строки. То, что вы можете сделать, это изменить разделитель на что-то еще, чтобы вы могли его искать позже.

Шаг 1:

Искать: "[^"\\]*(?:\\.[^"\\]*)*"

Заменить: $0Я

Шаг 2:

Найти: a(?=[^"\\]*(?:\\.[^"\\]*)*"Я)

Замените все, что вы ожидаете.

Шаг 3:

Поиск:

Замените ничем, чтобы вернуть все.


Ответ 2

Во-первых, несколько соображений:

  1. Там может быть несколько a символы в пределах одного предложения.
  2. Каждая цитата (с использованием одинарных или двойных кавычек) состоит из символа открытия кавычки, некоторого текста и того же символа закрытия кавычки. Простой подход состоит в том, чтобы предположить, что когда символы кавычек подсчитываются последовательно, нечетные - открывают кавычки, а четные - закрывают кавычки.
  3. Следуя пункту 2, можно подумать о том, следует ли допускать одиночные кавычки. См. Следующий пример: It a shame 'this quoted text' isn't quoted. Здесь простой подход предполагает, что существуют две строки: sa shame и isn. Другое: This isn't a quote...'this is' and 'it unclear where this quote ends'. Я избегал попытки справиться с этими сложностями и пошел с простым подходом ниже.

Плохая новость заключается в том, что точка 1 представляет собой небольшую проблему, так как группа захвата с символом повторного символа после него (например (.*)*) Будет захватывать только последнюю захваченную вещь. Но хорошая новость - это способ обойти это в определенных пределах. Многие двигатели регулярных выражений позволят до 99 захватить группы (*). Поэтому, если мы можем сделать предположение, что в каждой цитате будет не более 99 a (UPDATE... или даже если мы не сможем - см. Шаг 3), мы можем сделать следующее...

(*) К сожалению, мой первый порт захода, Notepad++ не делает - он разрешает только до 9. Не уверен в VS Code. Но regex101 (используется для онлайн-демонстраций ниже).

TL; DR - Что делать?

  1. Найти: "([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*" "([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*"
  2. Замените: "\1\2\3\4\5\6\7\8\9\10\11\12\13\14\15\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\32\33\34\35\36\37\38\39\40\41\42\43\44\45\46\47\48\49\50\51\52\53\54\55\56\57\58\59\60\61\62\63\64\65\66\67\68\69\70\71\72\73\74\75\76\77\78\79\80\81\82\83\84\85\86\87\88\89\90\91\92\93\94\95\96\97\98\99"
  3. (При необходимости повторяйте шаги предыдущих двух шагов, если есть возможность> 99 таких символов в одной кавычке, пока они не будут заменены).
  4. Повторите шаг 1, но заменив все " на ' в регулярном выражении, т.е.: '([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*' '([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*'
  5. Повторите шаги 2-3.

Интернет-демонстрации

Пожалуйста, ознакомьтесь со следующими демонстрациями regex101, которые действительно могут быть использованы для выполнения замен, если вы можете скопировать весь текст в содержимое "TEST STRING":

Ответ 3

/(["'])(.*?)(a)(.*?\1)/g

С шаблоном замены:

$1$2$4

Насколько мне известно, VS Code использует тот же механизм regex, что и JavaScript, поэтому я написал свой пример в JS.

Проблема в том, что если у вас есть несколько a в 1 наборе кавычек, тогда будет сложно вытащить правильные значения, поэтому за ним должен быть какой-то код, или вы, забивая кнопку замены, пока не будете больше найдены совпадения, чтобы вернуть шаблон и избавиться от всех a между кавычками

let regex = /(["'])(.*?)(a)(.*?\1)/g,
subst = '$1$2$4',
str = '"a"
"helapke"
Not matched - aaaaaaa
"This is the way the world ends"
"Not with fire"
"ABBA"
"abba",
'I can haz cheezburger'
"This is not a match'
';


// Loop to get rid of multiple a in quotes
while(str.match(regex)){
    str = str.replace(regex, subst);
}

const result = str;
console.log(result);

Ответ 4

Если вы можете использовать Visual Studio (вместо кода Visual Studio), он написан на C++ и С# и использует регулярные выражения.NET Framework, что означает, что вы можете использовать переменные длины lookbehinds для достижения этого.

(?<="[^"\n]*)a(?=[^"\n]*")

Добавление больше логики к выше регулярного выражения, мы можем сказать ему, чтобы игнорировать любые места, где Есть даже количество " предшествующих ему. Это не позволяет поиску a наружной кавычки. Возьмем, к примеру, строка "a" a "a". Только первая и последняя a в этой строке будут сопоставлены, но одна из них будет проигнорирована.

(?<!^[^"\n]*(?:(?:"[^"\n]*){2})+)(?<="[^"\n]*)a(?=[^"\n]*")

Теперь единственная проблема заключается в том, что это сломается, если мы избежим " в двух двойных кавычках, таких как "a\"" a "a". Нам нужно добавить больше логики, чтобы предотвратить это поведение. К счастью, этот красивый ответ существует для правильного соответствия экрану ". Добавляя эту логику к регулярному выражению выше, мы получаем следующее:

(?<!^[^"\n]*(?:(?:"(?:[^"\\\n]|\\.)*){2})+)(?<="[^"\n]*)a(?=[^"\n]*")

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

  • (?<!^[^"\n]*(?:(?:"(?:[^"\\\n]|\\.)*){2})+) Отрицательный lookbehind, обеспечивающий то, что предшествует, t соответствует следующему
    • ^ Позиция подтверждения в начале строки
    • [^"\n]* Совпадение ничего, кроме " или \n любое количество раз
    • (?:(?:"(?:[^"\\\n]|\\.)*){2})+ Сопоставьте следующие один или несколько раз. Это гарантирует, что есть какие-либо " предшествующие совпадению, которые они сбалансированы в том смысле, что есть открывающая и закрывающая двойная кавычка".
      • (?:"(?:[^"\\\n]|\\.)*){2} Сопоставьте следующее ровно дважды
      • " Совместите это буквально
      • (?:[^"\\\n]|\\.)* Сопоставьте любое из следующего числа раз
        • [^"\\\n] Сопоставьте все, кроме ", \ и \n
        • \\. Матчи \ сопровождаются любым символом
  • (?<="[^"\n]*) Положительный lookbehind, обеспечивающий то, что предшествует, соответствует следующему
    • " Совместите это буквально
    • [^"\n]* Совпадение ничего, кроме " или \n любое количество раз
  • матч это буквально
  • (?=[^"\n]*") Положительный взгляд, обеспечивающий то, что соответствует, соответствует следующему
    • [^"\n]* Совпадение ничего, кроме " или \n любое количество раз
    • " Совместите это буквально

Вы можете удалить \n из приведенного выше шаблона, как показано ниже. Я добавил его на случай, если у вас есть какие-то особые случаи, которые я не рассматриваю (например, комментарии), которые могут сломать это регулярное выражение в вашем тексте. Параметр \A также заставляет регулярное выражение соответствовать началу строки (или файла) вместо начала строки.

(?<!\A[^"]*(?:(?:"(?:[^"\\]|\\.)*){2})+)(?<="[^"]*)a(?=[^"]*")

Вы можете проверить это регулярное выражение здесь

Вот как это выглядит в Visual Studio:

Visual Studio example

Ответ 5

I am using VSCode, but I'm open to any suggestions.

Если вы хотите остаться в среде редактора, вы можете использовать
Visual Studio (> = 2012) или даже notepad++ для быстрого исправления.
Это позволяет избежать использования ложной среды сценариев.

Оба этих механизма (Dot-Net и boost, соответственно) используют конструкцию \G
Который запускает следующий матч в том положении, где последний остановился.

Опять же, это всего лишь предложение.

Это регулярное выражение не проверяет правильность сбалансированных котировок в течение всего
(возможно, с добавлением одной строки).

Все дело в том, чтобы знать, где внутри и снаружи цитаты.

Я прокомментировал регулярное выражение, но если вам нужна дополнительная информация, дайте мне знать.
Опять же, это всего лишь предложение (я знаю, что ваш редактор использует ECMAScript).

Найти (?s)(?:^([^"]*(?:"[^"a]*(?=")"[^"]*(?="))*"[^"a]*)|(?!^)\G)a([^"a]*(?:(?=a.*?")|(?:"[^"]*$|"[^"]*(?=")(?:"[^"a]*(?=")"[^"]*(?="))*"[^"a]*)))
Заменить $1b$2

Это все, что есть.

https://regex101.com/r/loLFYH/1

Комментарии

(?s)                          # Dot-all inine modifier
 (?:
      ^                             # BOS 
      (                             # (1 start), Find first quote from BOS (written back)
           [^"]* 
           (?:                           # --- Cluster
                " [^"a]*                      # Inside quotes with no 'a'
                (?= " )
                " [^"]*                       # Between quotes, get up to next quote
                (?= " )
           )*                            # --- End cluster, 0 to many times

           " [^"a]*                      # Inside quotes, will be an 'a' ahead of here
                                         # to be sucked up by this match           
      )                             # (1 end)

   |                              # OR,

      (?! ^ )                       # Not-BOS 
      \G                            # Continue where left off from last match.
                                    # Must be an 'a' at this point
 )
 a                             # The 'a' to be replaced

 (                             # (2 start), Up to the next 'a' (to be written back)
      [^"a]* 
      (?:                           # --------------------
           (?= a .*? " )                 # If stopped before 'a', must be a quote ahead
        |                              # or,
           (?:                           # --------------------
                " [^"]* $                     # If stopped at a quote, check for EOS
             |                              # or, 
                " [^"]*                       # Between quotes, get up to next quote
                (?= " )

                (?:                           # --- Cluster
                     " [^"a]*                      # Inside quotes with no 'a'
                     (?= " )
                     " [^"]*                       # Between quotes 
                     (?= " )
                )*                            # --- End cluster, 0 to many times

                " [^"a]*                      # Inside quotes, will be an 'a' ahead of here
                                              # to be sucked up on the next match                    
           )                             # --------------------
      )                             # --------------------
 )                             # (2 end)

Ответ 6

"Внутренние двойные кавычки" довольно сложны, потому что могут усложнить сценарии, чтобы рассмотреть возможность полностью автоматизировать это.

Каковы ваши точные правила для "заключенных в кавычки"? Вам нужно рассмотреть многострочные кавычки? Вы цитировали строки, содержащие экранированные кавычки или кавычки, используемые, кроме стартовой/конечной строковой цитаты?

Однако может быть довольно простое выражение, чтобы сделать многое из того, что вы хотите.

Выражение поиска: ("[^a"]*)a

Замена: $1b

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

Если вы можете жить с визуальным контролем, то мы можем создать этот шаблон, чтобы включить различные типы котировок и верхний и нижний регистр.