Làm thế nào để thay thế các chuỗi con không phân biệt chữ hoa chữ thường trong Java


130

Sử dụng phương thức replace(CharSequence target, CharSequence replacement)trong Chuỗi, làm cách nào tôi có thể làm cho trường hợp không phân biệt chữ hoa chữ thường?

Ví dụ, cách nó hoạt động ngay bây giờ:

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

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

Làm thế nào tôi có thể làm cho nó thay thế (hoặc nếu có một phương pháp phù hợp hơn) không phân biệt chữ hoa chữ thường để cả hai ví dụ trả về "Bar"?

Câu trả lời:


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

Đầu ra:

Bar

Điều đáng nói là replaceAllcoi đối số đầu tiên là mẫu biểu thức chính quy, có thể gây ra kết quả không mong muốn. Để giải quyết điều này, cũng sử dụng Pattern.quotenhư đề xuất trong các ý kiến.


1
Điều gì sẽ xảy ra nếu mục tiêu chứa các ký tự $ hoặc dấu phụ như á?
stracktracer

3
Ý tôi là hai điều: 1. "blÁÜ123" .replaceAll ("(? I) bláü") không thay thế bất cứ điều gì. 2. "Câu! Kết thúc" .replace ALL ("(? I) Câu.") Có thể thay thế nhiều hơn dự đoán.
stracktracer

1
Bạn không thể biến chuỗi thành regex khớp với nó đơn giản như vậy. Nói chung là không chính xác, nó sẽ chỉ hoạt động cho các trường hợp cụ thể.
Thủy thủ Danubian

19
Sử dụng Pattern.quote () để bảo vệ chuỗi tìm kiếm khỏi bị hiểu là regex. Doe snot này giải quyết các quirks unicode được liệt kê ở trên, nhưng sẽ ổn cho các bộ ký tự cơ bản. ví dụ target.replaceAll("(?i)"+Pattern.quote("foo"), "");
Jeff Adamson

1
Chỉ đảm bảo. Pattern.quote ("foo") là không cần thiết nếu chuỗi là "foo" phải không? Chỉ khi nó là một cái gì đó lạ mắt hơn, phải không?
ed22

10

Nếu bạn không quan tâm đến trường hợp, thì có lẽ bạn không thành vấn đề nếu nó trả lại tất cả các chữ hoa:

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

Bạn cũng có thể chuyển Locale vào toUpperCase (miền địa phương) nếu bạn xử lý các ký tự như á.
cướp

10

Không thanh lịch có lẽ như các phương pháp khác nhưng nó khá vững chắc và dễ làm theo, đặc biệt. cho những người mới hơn với Java. Một điều khiến tôi hiểu về lớp String là: Nó đã xuất hiện từ rất lâu và trong khi nó hỗ trợ thay thế toàn cầu bằng regrec và thay thế toàn cầu bằng String (thông qua CharSequences), cuối cùng không có tham số boolean đơn giản : 'isCaseInsensitive'. Thực sự, bạn đã nghĩ rằng chỉ bằng cách thêm một công tắc nhỏ đó, tất cả những rắc rối mà sự vắng mặt của nó gây ra cho người mới bắt đầu đặc biệt có thể tránh được. Bây giờ trên JDK 7, String vẫn không hỗ trợ thêm một chút này!

Dù sao, tôi sẽ ngừng nắm bắt. Đối với mọi người đặc biệt mới hơn với Java, đây là deus ex machina cắt và dán của bạn . Như tôi đã nói, không thanh lịch và sẽ không giành cho bạn bất kỳ giải thưởng mã hóa bóng bẩy nào, nhưng nó hoạt động và đáng tin cậy. Bất kỳ ý kiến, hãy đóng góp. (Vâng, tôi biết, StringBuffer có lẽ là một lựa chọn tốt hơn để quản lý hai dòng đột biến chuỗi ký tự, nhưng nó đủ dễ để trao đổi các kỹ thuật.)

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

phương pháp này hoàn toàn chậm vì độ phức tạp của nó là O (size_str * size_findtext)
Mladen Adamovic

9

Các biểu thức thông thường khá phức tạp để quản lý do thực tế là một số ký tự được dành riêng: ví dụ: "foo.bar".replaceAll(".")tạo ra một chuỗi rỗng, vì dấu chấm có nghĩa là "bất cứ thứ gì" Nếu bạn chỉ muốn thay thế điểm nên được chỉ định làm tham số "\\.".

Một giải pháp đơn giản hơn là sử dụng các đối tượng StringBuilder để tìm kiếm và thay thế văn bản. Phải mất hai: một có chứa văn bản trong phiên bản chữ thường trong khi thứ hai chứa phiên bản gốc. Tìm kiếm được thực hiện trên nội dung chữ thường và chỉ mục được phát hiện cũng sẽ thay thế văn bản gốc.

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
Hoạt động tuyệt vời! Lưu ý rằng "mục tiêu" không được rỗng. Việc xóa sbSourceLower không cần thiết (nữa).
msteiger

Cảm ơn giải pháp ngắn gọn và cảm ơn @msteiger đã sửa chữa. Tôi tự hỏi tại sao không ai thêm giải pháp tương tự cho bất kỳ lib nổi tiếng nào như Guava, Apache Commons, v.v.?
yetanothercoder

4

Đối với các ký tự không phải là Unicode:

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

4

org.apache.commons.lang3.StringUtils:

chuỗi tĩnh công khai thayIgnoreCase (Chuỗi văn bản, chuỗi tìm kiếm Chuỗi, thay thế chuỗi)

Case không nhạy cảm thay thế tất cả các lần xuất hiện của một Chuỗi trong một Chuỗi khác.


3

Tôi thích câu trả lời của smas sử dụng với một biểu thức thông thường. Nếu bạn sẽ thực hiện cùng một thay thế nhiều lần, sẽ rất hợp lý khi biên dịch trước biểu thức chính quy một lần: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

Chỉ cần làm cho nó đơn giản mà không cần thư viện của bên thứ ba:

    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));
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.