Как найти и заменить конкретный символ, но только если он находится в кавычках?
Проблема. У меня есть тысячи документов, которые содержат определенный символ, которого я не хочу. Например, символ 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
Трехэтапный подход
Последний, но тем не менее важный...
Совпадение символа внутри кавычек сложно, поскольку разделители точно такие же, что и открывающие и закрывающие метки нельзя отличить друг от друга, не обращая внимания на соседние строки. То, что вы можете сделать, это изменить разделитель на что-то еще, чтобы вы могли его искать позже.
Шаг 1:
Искать: "[^"\\]*(?:\\.[^"\\]*)*"
Заменить: $0Я
Шаг 2:
Найти: a(?=[^"\\]*(?:\\.[^"\\]*)*"Я)
Замените все, что вы ожидаете.
Шаг 3:
Поиск: "Я
Замените ничем, чтобы вернуть все.
Ответ 2
Во-первых, несколько соображений:
- Там может быть несколько
a
символы в пределах одного предложения. - Каждая цитата (с использованием одинарных или двойных кавычек) состоит из символа открытия кавычки, некоторого текста и того же символа закрытия кавычки. Простой подход состоит в том, чтобы предположить, что когда символы кавычек подсчитываются последовательно, нечетные - открывают кавычки, а четные - закрывают кавычки.
- Следуя пункту 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 - Что делать?
- Найти:
"([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*"
"([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*"
- Замените:
"\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"
- (При необходимости повторяйте шаги предыдущих двух шагов, если есть возможность> 99 таких символов в одной кавычке, пока они не будут заменены).
- Повторите шаг 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-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:
Ответ 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
Это не считается внутри или вне кавычек - вы делаете это визуально. Но он выделяет текст из цитаты соответствующему символу, поэтому вы можете быстро решить, находится ли это внутри или нет.
Если вы можете жить с визуальным контролем, то мы можем создать этот шаблон, чтобы включить различные типы котировок и верхний и нижний регистр.