String.replaceTất cả các dấu gạch chéo ngược đơn với dấu gạch chéo ngược kép


122

Tôi đang cố gắng chuyển đổi String \something\thành String \\something\\sử dụng replaceAll, nhưng tôi tiếp tục gặp phải tất cả các loại lỗi. Tôi nghĩ đây là giải pháp:

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

Nhưng điều này đưa ra ngoại lệ dưới đây:

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

Câu trả lời:


204

Tham số String#replaceAll()diễn giải đối số như một biểu thức chính quy . The \là một nhân vật thoát trong cả hai Stringregex. Bạn cần thoát kép nó cho regex:

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

Nhưng bạn không nhất thiết phải cần regex cho việc này, đơn giản vì bạn muốn thay thế chính xác từng ký tự và bạn không cần các mẫu ở đây. Vì vậy, String#replace()nên đủ:

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

Cập nhật : theo các nhận xét, dường như bạn muốn sử dụng chuỗi trong ngữ cảnh JavaScript. Có lẽ bạn nên sử dụng StringEscapeUtils#escapeEcmaScript()thay thế để bao gồm nhiều ký tự hơn.


Trên thực tế, nó được sử dụng trong JavaScript AST nên được chuyển đổi trở lại nguồn. Giải pháp của bạn hoạt động. Cảm ơn!
Frank Groeneveld

2
Nếu bạn muốn sử dụng String#replaceAll()dù sao, bạn có thể trích dẫn chuỗi thay thế với Matcher # quoteReplacement () :theString.replaceAll("\\", Matcher.quoteReplacement("\\\\"));
phse

Matcher.quoteReplacement (...) là một cách hay! Mời bạn xem câu trả lời của Pshemo!
Hartmut P.

14

Để tránh loại rắc rối này, bạn có thể sử dụng replace( sử dụng chuỗi đơn giản) thay vì ( sử dụng replaceAllbiểu thức chính quy). Bạn vẫn sẽ cần thoát khỏi dấu gạch chéo ngược, nhưng không phải theo những cách hoang dã được yêu cầu với biểu thức chính quy.


10

TLDR: sử dụng theString = theString.replace("\\", "\\\\");thay thế.


Vấn đề

replaceAll(target, replacement)sử dụng cú pháp biểu thức chính quy (regex) cho targetvà một phần cho replacement.

Vấn đề là \ký tự đặc biệt trong regex (nó có thể được sử dụng như \dđể biểu thị chữ số) và trong chuỗi ký tự (nó có thể được sử dụng "\n"để biểu thị dấu phân tách dòng hoặc \"để thoát khỏi ký hiệu dấu ngoặc kép thường biểu thị phần cuối của chuỗi ký tự).

Trong cả hai trường hợp này để tạo \biểu tượng, chúng ta có thể thoát khỏi nó (làm cho nó theo nghĩa đen thay vì ký tự đặc biệt) bằng cách đặt bổ sung \trước nó (giống như chúng ta thoát "trong chuỗi ký tự qua \").

Vì vậy, để biểu tượng targetđại diện cho regex \sẽ cần phải giữ \\, và chuỗi ký tự đại diện cho văn bản như vậy sẽ cần phải trông như thế nào "\\\\".

Vì vậy, chúng tôi đã trốn thoát \hai lần:

  • một lần trong regex \\
  • một lần trong chuỗi ký tự "\\\\"(mỗi ký tự \được biểu diễn dưới dạng "\\").

Trong trường hợp của replacement \cũng là đặc biệt ở đó. Nó cho phép chúng tôi thoát khỏi ký tự đặc biệt khác $thông qua $xký hiệu, cho phép chúng tôi sử dụng một phần dữ liệu được khớp bởi regex và được giữ bằng cách chụp nhóm được lập chỉ mục xnhư "012".replaceAll("(\\d)", "$1$1")sẽ khớp với từng chữ số, đặt nó vào nhóm 1 và $1$1sẽ thay thế nó bằng hai bản sao của nó (nó sẽ nhân bản nó) dẫn đến "001122".

Vì vậy, một lần nữa, để replacementđại diện cho \nghĩa đen, chúng ta cần phải loại bỏ nó bằng cách bổ sung \có nghĩa là:

  • thay thế phải giữ hai ký tự gạch chéo ngược \\
  • và chuỗi ký tự thể hiện \\trông giống như"\\\\"

NHƯNG vì chúng ta muốn replacementgiữ hai dấu gạch chéo ngược, chúng ta sẽ cần "\\\\\\\\"(mỗi dấu gạch chéo ngược được \biểu thị bằng một "\\\\").

Vì vậy, phiên bản với replaceAllcó thể trông giống như

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

Cách dễ dàng hơn

Để làm cho cuộc sống dễ dàng hơn, Java cung cấp các công cụ để tự động thoát văn bản vào targetreplacementcác phần. Vì vậy, bây giờ chúng ta có thể chỉ tập trung vào chuỗi và quên cú pháp regex:

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

trong trường hợp của chúng ta có thể trông như thế nào

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

Thậm chí còn tốt hơn

Nếu chúng ta không thực sự cần hỗ trợ cú pháp regex thì không nên liên quan replaceAllgì cả. Thay vào đó, hãy sử dụng replace. Cả hai phương thức sẽ thay thế tất cả các target s, nhưng replacekhông liên quan đến cú pháp regex. Vì vậy, bạn có thể chỉ cần viết

theString = theString.replace("\\", "\\\\");

7

Bạn sẽ cần phải thoát khỏi dấu gạch chéo ngược (đã thoát) trong đối số đầu tiên vì nó là một biểu thức chính quy. Thay thế (đối số thứ 2 - xem Matcher # ReplaceAll (Chuỗi) ) cũng có ý nghĩa đặc biệt của dấu gạch chéo ngược, vì vậy bạn sẽ phải thay thế chúng thành:

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

3

Đúng ... vào thời điểm trình biên dịch regex nhìn thấy mẫu bạn đã đưa ra, nó chỉ thấy một dấu gạch chéo ngược duy nhất (vì lexer của Java đã biến dấu gạch chéo ngược kép thành dấu gạch chéo ngược duy nhất). Bạn cần thay thế "\\\\"bằng "\\\\", tin hay không! Java thực sự cần một cú pháp chuỗi thô tốt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.