วิธีการแทนที่สตริงย่อยลิเทอรัลที่ไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ใน Java


130

เมื่อใช้วิธีการreplace(CharSequence target, CharSequence replacement)ใน String ฉันจะทำให้เป้าหมายไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ได้อย่างไร

ตัวอย่างเช่นวิธีการทำงานในขณะนี้:

String target = "FooBar";
target.replace("Foo", "") // would return "Bar"

String target = "fooBar";
target.replace("Foo", "") // would return "fooBar"

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

คำตอบ:


284
String target = "FOOBar";
target = target.replaceAll("(?i)foo", "");
System.out.println(target);

เอาท์พุท:

Bar

ควรค่าแก่การกล่าวถึงว่าreplaceAllถือว่าอาร์กิวเมนต์แรกเป็นรูปแบบ regex ซึ่งอาจทำให้เกิดผลลัพธ์ที่ไม่คาดคิด เพื่อแก้ปัญหานี้ให้ใช้Pattern.quoteตามที่แนะนำในความคิดเห็น


1
จะเกิดอะไรขึ้นถ้าเป้าหมายมี $ หรืออักขระกำกับเสียงเช่นá?
stracktracer

3
ฉันหมายถึงสองสิ่ง: 1. "blÁÜ123" .replaceAll ("(? i) bláü") ไม่ได้แทนที่อะไร 2. "Sentence! End" .replaceAll ("(? i) Sentence.") อาจจะแทนที่มากกว่าที่คาดการณ์ไว้
stracktracer

1
คุณไม่สามารถเปลี่ยนสตริงเป็นการจับคู่นิพจน์ทั่วไปได้ง่ายๆ โดยทั่วไปจะไม่ถูกต้อง แต่จะใช้ได้กับบางกรณีเท่านั้น
Danubian Sailor

19
ใช้ Pattern.quote () เพื่อป้องกันสตริงการค้นหาจากการตีความเป็นนิพจน์ทั่วไป น้ำมูก doe นี้ช่วยแก้ปัญหาของ Unicode ที่ระบุไว้ข้างต้น แต่ควรใช้ได้ดีสำหรับชุดอักขระพื้นฐาน เช่น target.replaceAll("(?i)"+Pattern.quote("foo"), "");
Jeff Adamson

1
เพียงแค่ตรวจสอบให้แน่ใจ Pattern.quote ("foo") ไม่จำเป็นถ้าสตริงเป็น "foo" ใช่ไหม? เพียงแต่ว่ามันเป็นอะไรที่แฟนซีกว่านี้ใช่มั้ย?
ed22


10

อาจจะไม่สวยหรูเท่าวิธีอื่น ๆ แต่ก็ค่อนข้างมั่นคงและทำตามได้ง่ายโดยเฉพาะ สำหรับผู้ที่ใหม่กว่ากับ Java สิ่งหนึ่งที่ทำให้ฉันเกี่ยวกับคลาส String คือสิ่งนี้มันมีมานานมากแล้วและในขณะที่มันรองรับ global แทนที่ด้วย regexp และ global แทนที่ด้วย Strings (ผ่าน CharSequences) สุดท้ายนั้นไม่มีพารามิเตอร์บูลีนง่ายๆ : 'isCaseInsensitive' จริงๆแล้วคุณคงคิดว่าเพียงแค่เพิ่มสวิตช์เล็ก ๆ ตัวเดียวก็สามารถหลีกเลี่ยงปัญหาทั้งหมดที่ไม่มีสำหรับผู้เริ่มต้นได้ ตอนนี้ใน JDK 7 String ยังไม่รองรับการเพิ่มเล็กน้อยนี้!

เอาล่ะฉันจะหยุดจับ สำหรับทุกคนที่เพิ่งเริ่มใช้ Java นี่คือdeus ex machinaแบบตัดแล้ววางของคุณ อย่างที่ฉันพูดไปไม่สวยหรูและจะไม่ชนะรางวัลการเขียนโค้ดที่ลื่นไหล แต่ใช้งานได้และเชื่อถือได้ ความคิดเห็นใด ๆ อย่าลังเลที่จะมีส่วนร่วม (ใช่ฉันรู้ว่า StringBuffer น่าจะเป็นทางเลือกที่ดีกว่าในการจัดการบรรทัดการกลายพันธุ์ของสตริงอักขระสองตัว แต่มันง่ายพอที่จะสลับเทคนิคได้)

public String replaceAll(String findtxt, String replacetxt, String str, 
        boolean isCaseInsensitive) {
    if (str == null) {
        return null;
    }
    if (findtxt == null || findtxt.length() == 0) {
        return str;
    }
    if (findtxt.length() > str.length()) {
        return str;
    }
    int counter = 0;
    String thesubstr = "";
    while ((counter < str.length()) 
            && (str.substring(counter).length() >= findtxt.length())) {
        thesubstr = str.substring(counter, counter + findtxt.length());
        if (isCaseInsensitive) {
            if (thesubstr.equalsIgnoreCase(findtxt)) {
                str = str.substring(0, counter) + replacetxt 
                    + str.substring(counter + findtxt.length());
                // Failing to increment counter by replacetxt.length() leaves you open
                // to an infinite-replacement loop scenario: Go to replace "a" with "aa" but
                // increment counter by only 1 and you'll be replacing 'a's forever.
                counter += replacetxt.length();
            } else {
                counter++; // No match so move on to the next character from
                           // which to check for a findtxt string match.
            }
        } else {
            if (thesubstr.equals(findtxt)) {
                str = str.substring(0, counter) + replacetxt 
                    + str.substring(counter + findtxt.length());
                counter += replacetxt.length();
            } else {
                counter++;
            }
        }
    }
    return str;
}

วิธีนี้ช้าที่สุดเนื่องจากความซับซ้อนคือ O (size_str * size_findtext)
Mladen Adamovic

9

การแสดงออกปกติจะค่อนข้างซับซ้อนในการจัดการเนื่องจากความจริงที่ว่าตัวละครบางตัวจะถูกสงวนไว้: ยกตัวอย่างเช่น"foo.bar".replaceAll(".")ผลิตสตริงที่ว่างเปล่าเพราะจุดหมายถึง "อะไร" "\\."ถ้าคุณต้องการที่จะเปลี่ยนเพียงจุดที่ควรจะระบุเป็นพารามิเตอร์

วิธีแก้ปัญหาที่ง่ายกว่าคือการใช้วัตถุ StringBuilder เพื่อค้นหาและแทนที่ข้อความ ใช้เวลาสอง: หนึ่งที่มีข้อความเป็นตัวพิมพ์เล็กในขณะที่สองประกอบด้วยเวอร์ชันดั้งเดิม การค้นหาจะดำเนินการกับเนื้อหาตัวพิมพ์เล็กและดัชนีที่ตรวจพบจะแทนที่ข้อความต้นฉบับด้วย

public class LowerCaseReplace 
{
    public static String replace(String source, String target, String replacement)
    {
        StringBuilder sbSource = new StringBuilder(source);
        StringBuilder sbSourceLower = new StringBuilder(source.toLowerCase());
        String searchString = target.toLowerCase();

        int idx = 0;
        while((idx = sbSourceLower.indexOf(searchString, idx)) != -1) {
            sbSource.replace(idx, idx + searchString.length(), replacement);
            sbSourceLower.replace(idx, idx + searchString.length(), replacement);
            idx+= replacement.length();
        }
        sbSourceLower.setLength(0);
        sbSourceLower.trimToSize();
        sbSourceLower = null;

        return sbSource.toString();
    }


    public static void main(String[] args)
    {
        System.out.println(replace("xXXxyyyXxxuuuuoooo", "xx", "**"));
        System.out.println(replace("FOoBaR", "bar", "*"));
    }
}

1
ใช้งานได้ดี! โปรดทราบว่า "เป้าหมาย" ต้องไม่เป็นค่าว่าง ไม่จำเป็นต้องล้าง sbSourceLower (อีกต่อไป)
msteiger

ขอบคุณสำหรับวิธีแก้ปัญหาที่กระชับและขอบคุณ @msteiger สำหรับการแก้ไข ฉันสงสัยว่าทำไมไม่มีใครเพิ่มโซลูชันที่คล้ายกันกับ lib ที่มีชื่อเสียงเช่น Guava, Apache Commons เป็นต้น?
yetanothercoder

4

สำหรับอักขระที่ไม่ใช่ Unicode:

String result = Pattern.compile("(?i)препарат", 
Pattern.UNICODE_CASE).matcher(source).replaceAll("БАД");

4

org.apache.commons.lang3.StringUtils:

สตริงสาธารณะแบบคงที่ replaceIgnoreCase (ข้อความสตริง, สตริงค้นหาสตริง, การแทนที่สตริง)

ตัวพิมพ์เล็กและใหญ่แทนที่การเกิดขึ้นทั้งหมดของสตริงภายในสตริงอื่น


3

ฉันชอบคำตอบของsmasที่ใช้กับนิพจน์ทั่วไป หากคุณจะทำการแทนที่แบบเดียวกันหลาย ๆ ครั้งคุณควรคอมไพล์นิพจน์ทั่วไปล่วงหน้าหนึ่งครั้ง:replaceAll

import java.util.regex.Pattern;

public class Test { 

    private static final Pattern fooPattern = Pattern.compile("(?i)foo");

    private static removeFoo(s){
        if (s != null) s = fooPattern.matcher(s).replaceAll("");
        return s;
    }

    public static void main(String[] args) {
        System.out.println(removeFoo("FOOBar"));
    }
}

3

ทำให้ง่ายโดยไม่ต้องใช้ไลบรารีของบุคคลที่สาม:

    final String source = "FooBar";
    final String target = "Foo";
    final String replacement = "";
    final String result = Pattern.compile(target, Pattern.LITERAL | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE).matcher(source)
.replaceAll(Matcher.quoteReplacement(replacement));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.