String.replace แบ็กสแลชเดี่ยวทั้งหมดที่มีแบ็กสแลชคู่


122

ฉันพยายามที่จะแปลงString \something\ลงในString \\something\\การใช้replaceAllแต่ฉันให้ได้รับทุกข้อผิดพลาด ฉันคิดว่านี่คือทางออก:

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

แต่สิ่งนี้ให้ข้อยกเว้นด้านล่าง:

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

คำตอบ:


204

String#replaceAll()ตีความอาร์กิวเมนต์เป็นนิพจน์ปกติ \เป็นตัวหนีในทั้งสอง และString regexคุณต้องหนีสองครั้งสำหรับ regex:

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

แต่คุณไม่จำเป็นต้องใช้ regex สำหรับสิ่งนี้เพียงเพราะคุณต้องการการแทนที่ทีละอักขระและคุณไม่ต้องการรูปแบบที่นี่ ดังนั้นString#replace()ควรพอเพียง:

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

อัปเดต : ตามความคิดเห็นดูเหมือนว่าคุณต้องการใช้สตริงในบริบท JavaScript คุณอาจจะดีกว่าใช้StringEscapeUtils#escapeEcmaScript()แทนเพื่อปกปิดตัวละครเพิ่มเติม


จริงๆแล้วมันถูกใช้ใน JavaScript AST ที่ควรถูกแปลงกลับเป็นซอร์ส โซลูชันของคุณใช้ได้ผล ขอบคุณ!
Frank Groeneveld

2
หากคุณต้องการใช้String#replaceAll()ต่อไปคุณสามารถอ้างอิงสตริงแทนที่ด้วยMatcher # quoteReplacement () :theString.replaceAll("\\", Matcher.quoteReplacement("\\\\"));
phse

Matcher.quoteReplacement (... ) เป็นวิธีที่ดี! โปรดดูคำตอบของ Pshemo!
Hartmut P.

14

เพื่อหลีกเลี่ยงปัญหาประเภทนี้คุณสามารถใช้replace(ซึ่งใช้สตริงธรรมดา) แทนreplaceAll(ซึ่งใช้เป็นนิพจน์ทั่วไป) คุณยังคงต้องหลีกเลี่ยงแบ็กสแลช แต่ไม่จำเป็นต้องใช้นิพจน์ทั่วไป


10

TLDR: ใช้theString = theString.replace("\\", "\\\\");แทน


ปัญหา

replaceAll(target, replacement)ใช้การแสดงออกปกติ (regex) ไวยากรณ์สำหรับและบางส่วน targetreplacement

ปัญหาคือ\อักขระพิเศษใน regex (สามารถใช้\dเพื่อแทนตัวเลข) และใน String literal (สามารถใช้"\n"เพื่อแสดงตัวคั่นบรรทัดหรือ\"เพื่อหลีกเลี่ยงสัญลักษณ์เครื่องหมายคำพูดคู่ซึ่งโดยปกติจะแสดงจุดสิ้นสุดของสตริงตามตัวอักษร)

ในทั้งสองกรณีนี้ในการสร้าง\สัญลักษณ์เราสามารถหลีกเลี่ยงมันได้ (ทำให้เป็นตัวอักษรแทนอักขระพิเศษ) โดยวางเพิ่มเติมไว้ข้าง\หน้า (เช่นเราหลบหนี"ในตัวอักษรสตริงผ่าน\")

ดังนั้นในการtargetregex ที่แสดง\สัญลักษณ์จะต้องมีค้างไว้\\และสตริงตามตัวอักษรที่แสดงข้อความดังกล่าวจะต้องมีลักษณะ"\\\\"ดังนี้

เราจึงหนี\สองครั้ง:

  • ครั้งเดียวใน regex \\
  • หนึ่งครั้งใน String literal "\\\\"(แต่ละตัว\จะแสดงเป็น"\\")

ในกรณีreplacement \พิเศษยังมี ช่วยให้เราสามารถหลีกเลี่ยงอักขระพิเศษอื่น ๆ$ซึ่งผ่าน$xสัญกรณ์ช่วยให้เราใช้ส่วนของข้อมูลที่จับคู่โดย regex และถือครองโดยการจับกลุ่มที่จัดทำดัชนีเนื่องจากxเหมือน"012".replaceAll("(\\d)", "$1$1")จะตรงกับแต่ละหลักวางไว้ในการจับกลุ่ม 1 และ$1$1จะแทนที่ด้วยสองสำเนา (มันจะซ้ำ) "001122"ที่เกิดขึ้นใน

ดังนั้นอีกครั้งเพื่อให้replacementเป็นตัวแทน\ตามตัวอักษรเราจำเป็นต้องหลีกเลี่ยงมันด้วยเพิ่มเติม\ซึ่งหมายความว่า:

  • การแทนที่ต้องมีอักขระเครื่องหมายทับขวาสองตัว \\
  • และสตริงลิเทอรัลซึ่งแสดงถึง\\ลักษณะ"\\\\"

แต่เนื่องจากเราต้องการreplacementถือแบ็กสแลชสองตัวเราจึงจำเป็นต้องใช้"\\\\\\\\"(แต่ละตัว\แสดงด้วยหนึ่ง"\\\\")

ดังนั้นเวอร์ชันที่มีreplaceAllลักษณะเช่นนี้

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

วิธีที่ง่ายกว่า

เพื่อให้ชีวิตง่ายขึ้น Java มีเครื่องมือในการหลีกเลี่ยงข้อความเข้าtargetและreplacementส่วนต่างๆโดยอัตโนมัติ ตอนนี้เราสามารถมุ่งเน้นเฉพาะสตริงและลืมเกี่ยวกับไวยากรณ์ regex:

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

ซึ่งในกรณีของเราอาจมีลักษณะดังนี้

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

ดียิ่งขึ้น

หากเราไม่ต้องการการสนับสนุนไวยากรณ์ regex จริงๆอย่าให้เกี่ยวข้องreplaceAllเลย replaceแทนที่จะช่วยให้การใช้งาน ทั้งสองวิธีจะแทนที่s ทั้งหมด targetแต่replaceไม่เกี่ยวข้องกับไวยากรณ์ regex คุณก็เขียนได้

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

7

คุณจะต้องหนีเครื่องหมายแบ็กสแลช (Escape) ในอาร์กิวเมนต์แรกเนื่องจากเป็นนิพจน์ทั่วไป การแทนที่ (อาร์กิวเมนต์ที่ 2 - ดูMatcher # replaceAll (String) ) ยังมีความหมายพิเศษของแบ็กสแลชดังนั้นคุณจะต้องแทนที่สิ่งเหล่านี้เป็น:

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

3

ใช่ ... เมื่อถึงเวลาที่คอมไพเลอร์ regex เห็นรูปแบบที่คุณกำหนดมันจะเห็นเพียงแบ็กสแลชเดียว (เนื่องจาก lexer ของ Java ได้เปลี่ยนแบ็คแว็กคู่ให้กลายเป็นแบบเดียว) ต้องเปลี่ยน"\\\\"ด้วย"\\\\"เชื่อหรือไม่! Java ต้องการไวยากรณ์สตริงดิบที่ดีจริงๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.