String.replaceSemua garis miring terbalik dengan garis miring terbalik ganda

122

Saya mencoba mengubahnya String \something\menjadi String \\something\\penggunaan replaceAll, tetapi saya terus mendapatkan semua jenis kesalahan. Saya pikir inilah solusinya:

theString.replaceAll("\\", "\\\\");

Tetapi ini memberikan pengecualian di bawah ini:

java.util.regex.PatternSyntaxException: Unexpected internal error near index 1
Frank Groeneveld
sumber

Jawaban:

204

The String#replaceAll()menafsirkan argumen sebagai ekspresi reguler . Itu \adalah karakter pelarian di kedua String dan regex. Anda perlu meloloskan diri dua kali untuk regex:

string.replaceAll("\\\\", "\\\\\\\\");

Tetapi Anda tidak perlu regex untuk ini, hanya karena Anda menginginkan penggantian karakter demi karakter yang tepat dan Anda tidak memerlukan pola di sini. Jadi String#replace()seharusnya sudah cukup:

string.replace("\\", "\\\\");

Pembaruan : sesuai komentar, Anda tampaknya ingin menggunakan string dalam konteks JavaScript. Anda mungkin lebih baik menggunakan StringEscapeUtils#escapeEcmaScript()untuk menutupi lebih banyak karakter.

BalusC
sumber
Sebenarnya, ini digunakan dalam AST JavaScript yang harus diubah kembali ke sumber. Solusi Anda berhasil. Terima kasih!
Frank Groeneveld
2
Jika Anda tetap ingin menggunakannya String#replaceAll(), Anda dapat mengutip string pengganti dengan Matcher # quoteReplacement () :theString.replaceAll("\\", Matcher.quoteReplacement("\\\\"));
phse
Matcher.quoteReplacement (...) adalah cara yang bagus! Silakan lihat jawaban Pshemo!
Hartmut P.
14

Untuk menghindari masalah seperti ini, Anda dapat menggunakan replace(yang menggunakan string biasa) daripada replaceAll(yang menggunakan ekspresi reguler). Anda masih perlu mengosongkan garis miring terbalik, tetapi tidak dengan cara liar yang diperlukan dengan ekspresi reguler.

Fabian Steeg
sumber
10

TLDR: gunakan theString = theString.replace("\\", "\\\\");saja.


Masalah

replaceAll(target, replacement)menggunakan sintaks ekspresi reguler (regex) untuk targetdan sebagian untukreplacement .

Masalahnya adalah \karakter khusus dalam ekspresi reguler (dapat digunakan seperti \dmewakili digit) dan dalam string literal (dapat digunakan seperti "\n"untuk mewakili pemisah garis atau\" untuk melepaskan simbol kutip ganda yang biasanya mewakili akhir string literal).

Dalam kedua kasus ini untuk membuat \simbol, kita dapat menghindarinya (menjadikannya literal alih-alih karakter khusus) dengan menempatkan tambahan \sebelum itu (seperti kita melarikan diri "dalam literal string melalui \").

Jadi untuk targetekspresi reguler, \simbol perlu ditahan \\, dan string literal yang mewakili teks seperti itu perlu terlihat "\\\\".

Jadi kami lolos \dua kali:

  • sekali dalam regex \\
  • sekali dalam String literal "\\\\" (masing \- masing direpresentasikan sebagai "\\").

Dalam hal replacement \juga ada yang istimewa. Ini memungkinkan kita untuk keluar dari karakter khusus lainnya $yang melalui $xnotasi, memungkinkan kita untuk menggunakan bagian dari data yang cocok dengan regex dan dipegang dengan menangkap grup yang diindeks sebagaix , seperti "012".replaceAll("(\\d)", "$1$1")akan mencocokkan setiap digit, menempatkannya dalam menangkap grup 1 dan $1$1akan menggantinya dengan dua salinannya (itu akan menduplikasinya) menghasilkan"001122" .

Jadi sekali lagi, biarkan replacementmewakili\ literal kita perlu menghindarinya dengan tambahan \yang berarti:

  • pengganti harus memiliki dua karakter garis miring terbalik \\
  • dan String literal yang merepresentasikan \\tampilannya"\\\\"

TAPI karena kami ingin replacementmenahan dua garis miring terbalik, kami membutuhkan "\\\\\\\\"(masing-masing\ diwakili oleh satu"\\\\" ).

Jadi versi dengan replaceAllbisa terlihat seperti

replaceAll("\\\\", "\\\\\\\\");

Cara yang lebih mudah

Untuk membuat hidup lebih mudah, Java menyediakan alat untuk secara otomatis memasukkan teks ke dalam targetdan replacementbagian. Jadi sekarang kita bisa fokus hanya pada string, dan lupakan sintaks regex:

replaceAll(Pattern.quote(target), Matcher.quoteReplacement(replacement))

yang dalam kasus kita bisa terlihat seperti ini

replaceAll(Pattern.quote("\\"), Matcher.quoteReplacement("\\\\"))

Bahkan lebih baik

Jika kita tidak benar-benar membutuhkan dukungan sintaks regex, mari kita tidak terlibat replaceAllsama sekali. Sebaliknya mari kita gunakan replace. Kedua metode akan menggantikan semua target s, tetapi replacetidak melibatkan sintaks regex. Jadi Anda bisa langsung menulis

theString = theString.replace("\\", "\\\\");
Pshemo
sumber
7

Anda harus melepaskan garis miring terbalik (escaped) di argumen pertama karena ini adalah ekspresi reguler. Penggantian (argumen ke-2 - lihat Matcher # replaceAll (String) ) juga memiliki arti khusus garis miring terbalik, jadi Anda harus menggantinya ke:

theString.replaceAll("\\\\", "\\\\\\\\");
sfussenegger.dll
sumber
3

Ya ... pada saat regex compiler melihat pola yang Anda berikan, ia hanya melihat satu garis miring terbalik (karena lexer Java telah mengubah double backwhack menjadi satu). Anda perlu menggantinya "\\\\"dengan "\\\\", percaya atau tidak! Java sangat membutuhkan sintaks string mentah yang baik.

Jonathan Feinberg
sumber