TLDR: użyj theString = theString.replace("\\", "\\\\");
zamiast tego.
Problem
replaceAll(target, replacement)
używa składni wyrażeń regularnych (regex) dla target
i częściowo dla replacement
.
Problem polega na tym, że \
jest to znak specjalny w wyrażeniu regularnym (może być używany tak jak \d
do reprezentacji cyfry) i w literale String (może być używany jak "\n"
do reprezentowania separatora linii lub \"
do ucieczki przed symbolem podwójnego cudzysłowu, który normalnie oznaczałby koniec literału ciągu).
W obu tych przypadkach, aby stworzyć \
symbol, możemy przed nim uciec (uczynić go dosłownym zamiast znaku specjalnego), umieszczając \
przed nim dodatkowe (tak jak "
w przypadku literałów ciągu przez \"
).
Tak więc target
regex reprezentujący \
symbol będzie musiał się trzymać \\
, a literał łańcuchowy reprezentujący taki tekst będzie musiał wyglądać "\\\\"
.
Więc uciekliśmy \
dwa razy:
- raz w wyrażeniu regularnym
\\
- raz w literale String
"\\\\"
(każdy \
jest reprezentowany jako "\\"
).
W przypadku replacement
\
jest też tam wyjątkowy. Pozwala nam uciec przed innym znakiem specjalnym, $
który za pomocą $x
notacji pozwala nam użyć części danych dopasowanych przez wyrażenie regularne i przechowywanych przez grupę przechwytywania indeksowaną tak x
, jak "012".replaceAll("(\\d)", "$1$1")
będzie pasowała do każdej cyfry, umieści ją w grupie przechwytywania 1 i $1$1
zastąpi ją dwoma kopiami (zduplikuje to), w wyniku czego "001122"
.
Więc znowu, aby replacement
reprezentować \
dosłownie, musimy uciec od tego dodatkowym, \
co oznacza, że:
- zamiennik musi zawierać dwa znaki ukośnika odwrotnego
\\
- i literał String, który reprezentuje
\\
wygląd"\\\\"
ALE ponieważ chcemy replacement
trzymać dwa ukośniki odwrotne, których będziemy potrzebować "\\\\\\\\"
(każdy \
reprezentowany przez jeden "\\\\"
).
Więc wersja z replaceAll
może wyglądać
replaceAll("\\\\", "\\\\\\\\");
Łatwiejszy sposób
Aby ułatwić życie, Java udostępnia narzędzia do automatycznego wprowadzania tekstu do target
i replacement
części. Więc teraz możemy skupić się tylko na łańcuchach i zapomnieć o składni regex:
replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement))
co w naszym przypadku może wyglądać
replaceAll(Pattern.quote("\\"), Matcher.quoteReplacement("\\\\"))
Nawet lepiej
Jeśli naprawdę nie potrzebujemy obsługi składni wyrażeń regularnych, nie angażujmy się replaceAll
w ogóle. Zamiast tego użyjmy replace
. Obie metody zastąpią wszystkie target
s, ale replace
nie obejmują składni wyrażeń regularnych. Możesz więc po prostu napisać
theString = theString.replace("\\", "\\\\");