Java'da büyük / küçük harfe duyarlı olmayan değişmez alt dizeler nasıl değiştirilir


130

replace(CharSequence target, CharSequence replacement)String'deki yöntemi kullanarak, hedefi büyük / küçük harfe duyarlı hale nasıl getirebilirim?

Örneğin, şu anki çalışma şekli:

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

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

Nasıl değiştirebilirim (veya daha uygun bir yöntem varsa) büyük / küçük harfe duyarlı değildir, böylece her iki örnek de "Bar" döndürür?

Yanıtlar:


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

Çıktı:

Bar

replaceAllİlk argümanı, beklenmedik sonuçlara neden olabilecek bir normal ifade kalıbı olarak ele aldığından bahsetmeye değer . Bunu çözmek Pattern.quoteiçin yorumlarda önerildiği gibi kullanın .


1
Hedef $ veya á gibi aksanlı karakterler içeriyorsa ne olur?
stracktracer

3
İki şeyi kastediyorum: 1. "blÁÜ123" .replaceAll ("(? İ) bláü") hiçbir şeyin yerini almaz. 2. "Cümle! Son" .replaceAll ("(? İ) Cümle.") Tahmin edilenden daha fazlasını değiştirebilir.
stracktracer

1
Dizeyi bu kadar basit eşleşen normal ifadeye dönüştüremezsiniz. Genel olarak doğru değildir, yalnızca belirli durumlarda işe yarar.
Danubian Sailor

19
Arama dizesinin bir normal ifade olarak yorumlanmasını önlemek için Pattern.quote () kullanın. Bu doe snot, yukarıda listelenen unicode tuhaflıklarına hitap eder, ancak temel karakter kümeleri için uygun olmalıdır. örneğin target.replaceAll("(?i)"+Pattern.quote("foo"), "");
Jeff Adamson

1
Sadece emin olmak için. Dize "foo" ise Pattern.quote ("foo") gerekli değildir değil mi? Sadece daha süslü bir şeyse, değil mi?
ed22

10

Vakayı umursamıyorsanız, belki de tamamen büyük harflerle dönüp dönmemesi önemli değildir:

target.toUpperCase().replace("FOO", "");

Á gibi karakterlerle uğraşırsanız, Yerel Ayarı UpperCase'e (yerel ayar) da iletebilirsiniz.
soymak

10

Belki diğer yaklaşımlar kadar zarif değil ama oldukça sağlam ve takip etmesi kolay, özellikle. Java'ya yeni başlayanlar için. String sınıfıyla ilgili beni anlayan bir şey şudur: Çok uzun zamandır etrafta dolaşıyor ve regexp ile genel bir değiştirmeyi ve Strings ile (CharSequences aracılığıyla) genel bir değiştirmeyi desteklerken, sonuncusu basit bir boole parametresine sahip değildir. : 'isCaseInsensitive'. Gerçekten, sadece o küçük anahtarı ekleyerek, yokluğunun özellikle yeni başlayanlar için neden olduğu tüm sorunların önlenebileceğini düşünmüştünüz. Şimdi JDK 7'de, String hala bu küçük eklemeyi desteklemiyor!

Neyse, kavraymayı bırakacağım. Özellikle Java'da daha yeni olan herkes için, işte sizin kesme ve yapıştırma deus ex machina . Dediğim gibi, bu kadar zarif değil ve size herhangi bir kaygan kodlama ödülü kazandırmayacak, ancak işe yarıyor ve güvenilir. Herhangi bir yorum, katkıda bulunmaktan çekinmeyin. (Evet, biliyorum, StringBuffer muhtemelen iki karakter dizisi mutasyon satırını yönetmek için daha iyi bir seçimdir, ancak teknikleri değiştirmek yeterince kolaydır.)

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;
}

karmaşıklığı O olduğu için bu yöntem tamamen yavaştır (size_str * size_findtext)
Mladen Adamovic

9

Bazı karakterlerin ayrılmış olması nedeniyle normal ifadeleri yönetmek oldukça karmaşıktır: örneğin, "foo.bar".replaceAll(".")nokta "herhangi bir şey" anlamına geldiği için boş bir dize üretir. Değiştirmek istiyorsanız, yalnızca noktayı parametre olarak belirtmelisiniz "\\.".

Daha basit bir çözüm, metni aramak ve değiştirmek için StringBuilder nesnelerini kullanmaktır. İki tane alır: Biri küçük harfli metni içerirken, ikincisi orijinal sürümü içerir. Arama, küçük harfli içeriklerde gerçekleştirilir ve tespit edilen dizin de orijinal metnin yerini alır.

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
Harika çalışıyor! "Hedef" in boş olmaması gerektiğini unutmayın. SbSourceLower'ın temizlenmesi gerekli olmamalıdır (artık).
msteiger

Kısa çözüm için teşekkürler ve düzeltme için @ msteiger'e teşekkürler. Acaba neden kimse Guava, Apache Commons vb. Gibi ünlü kitaplara benzer bir çözüm eklemedi?
yetanothercoder

4

Unicode olmayan karakterler için:

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

4

org.apache.commons.lang3.StringUtils:

public static String replaceIgnoreCase (String text, String searchString, String değişimi)

Büyük / küçük harf, bir String'in başka bir String içindeki tüm oluşumlarını duyarsız olarak değiştirir.


3

Sevdiğim smas 'ın cevabı kullanımları bu replaceAllnormal bir ifade ile. Aynı değişikliği birçok kez yapacaksanız, normal ifadeyi bir kez önceden derlemek mantıklıdır:

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

Üçüncü taraf kitaplıklar olmadan basitleştirin:

    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));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.