Если удаляется только )
в конце строки, то это работает:
str.replaceFirst("\\)$", "");
Это в точности соответствует тому, что написано: литерал )
(экранированный, потому что это также метасимвол регулярного выражения), за которым следует $
, привязка границы конца строки, и замена его пустой строкой, эффективно удаляя любые окончание )
.
Если совпадений нет, это означает, что в конце строки нет )
(даже если в другом месте могут быть вхождения), а замена не производится и строка не изменяется.
Если вы обычно хотите удалить последнее вхождение )
, которое может отсутствовать в конце строки, вы можете использовать жадное совпадение .*
:
str.replaceFirst("(.*)\\)", "$1");
Здесь мы имеем жадное совпадение .*
, которое фиксируется в \1
. Если бы весь шаблон когда-либо совпадал, \1
был бы настолько длинным, насколько это возможно, что означает, что литерал )
, следующий за ним, должен был быть последним вхождением (потому что, если в его случае есть другое вхождение верно, \1
мог бы вместо этого захватить более длинную строку, что противоречит).
Производительность
Соответствие первому регулярному выражению должно быть оптимизируемым для операции O(1)
, благодаря привязке конца строки $
. Фактическая замена будет O(N)
, потому что новая строка должна быть скопирована в новый буфер, если есть совпадение. Если совпадения нет, то должно быть оптимизируемым для возврата исходной строки, и, следовательно, было бы O(1)
в целом. Это настолько оптимально, насколько это возможно.
Второму регулярному выражению требуется O(N)
, чтобы соответствовать из-за повторения. Это не хуже, чем линейный поиск последних )
с использованием lastIndexOf
, который также O(N)
.
Если вы делаете это много, то вы должны знать стандартную скомпилированную Pattern
эквивалентность replaceFirst
. От API :
Вызов этого метода в форме
str.replaceFirst(regex, repl)
дает точно такой же результат, как выражение
Pattern.compile(regex).matcher(str).replaceFirst(repl)
читаемость
"Вызов метода replaceFirst
, который был взломан для фактической замены последнего, просто сбивает с толку."
Здесь следует указать, что на самом деле вы можете использовать replaceAll
с этими точными шаблонами, и решение все равно будет работать! На самом деле вам просто нужно заменить регулярное выражение, и либо из replaceAll
или replaceFirst
это действительно не имеет значения, шаблон действительно такой простой !
needle$
для сопоставления в конце строки и жадный (.*)needle
для сопоставления с последним вхождением - это основные идиомы, которые очень удобочитаемы и понятны для тех, кто имеет базовое понимание регулярных выражений. Ни один из них на самом деле не может считаться «хаком».
Использование метода под названием replaceFirst
для замены последнего вхождения чего-либо может сначала показаться вводящим в заблуждение, но это недальновидно: это первое совпадение шаблона , которое заменены; соответствие этому шаблону может быть чем угодно, будь то шестым "Sense"
или последним "Mohican"
!
В качестве аналогии, давайте возьмем другой простой пример манипуляции со строками: удалите все подстроки "spam"
из строки. Я бы сказал, что наиболее читаемым решением является использование replace
str.replace("spam", "");
"Но подождите! Имя replace
вводит в заблуждение! Вы не заменяете его чем-то другим! Вам следует вызвать метод с именем delete
или что-то еще!"
Это глупо, конечно! Вы действительно заменяете это чем-то другим - пустой строкой! Его эффект - удаление, но операция все еще строковая replace
-ment!
Точно так же, как replaceFirst
в моем решении: вы можете заменить последнее вхождение чего-либо, но это все же первое совпадение общего шаблона!
Теперь верно, что шаблон регулярного выражения из ниоткуда будет сбивать с толку, но это может быть ясно из контекста, например:
public static String removeLastCloseParenthesis(String str) {
return str.replaceFirst("(.*)\\)", "$1");
}
И вы всегда можете просто назвать вещь . И вы всегда можете поставить комментарии как / при необходимости . Это всего лишь общие методы читабельности кода, и поэтому они применимы к регулярным выражениям так же, как и ко всему остальному.