[java] Java에서 문자열에 대소 문자를 구분하지 않고 다른 문자열이 포함되어 있는지 확인하는 방법은 무엇입니까?

두 줄이 있다고 가정 해 봅시다.

String s1 = "AbBaCca";
String s2 = "bac";

s2포함 된 수표 반환을 수행하고 싶습니다 s1. 나는 이것을 할 수있다 :

return s1.contains(s2);

contains()대소 문자를 구분 한다고 확신 하지만 문서를 읽음으로써 이것을 결정할 수는 없습니다. 그렇다면 최선의 방법은 다음과 같습니다.

return s1.toLowerCase().contains(s2.toLowerCase());

이 모든 것을 제쳐두고 대소 문자를 구분하지 않고 이것을 달성 할 수있는 또 다른 방법이 있습니까?



답변

예, 대소 문자를 구분합니다. 대소 문자를 구분하지 않는 일치를 위해 java.util.regex.Pattern을 CASE_INSENSITIVE 플래그와 함께 사용할 수 있습니다.

Pattern.compile(Pattern.quote(wantedStr), Pattern.CASE_INSENSITIVE).matcher(source).find();

편집 : s2에 정규 표현식 특수 문자 (많은 문자가 포함되어 있음)가 포함되어 있으면 먼저 따옴표로 묶는 것이 중요합니다. 나는 사람들이 처음 보게 될 것이기 때문에 내 대답을 수정했지만 Matt Quail이 이것을 지적한 후에 투표하십시오.


답변

Dave L.의 답변에 대한 한 가지 문제 는 s2에와 같은 정규 표현식 마크 업이 포함되어있을 때입니다 \d.

s2에서 Pattern.quote ()를 호출하려고합니다.

Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();


답변

당신이 사용할 수있는

org.apache.commons.lang3.StringUtils.containsIgnoreCase("AbBaCca", "bac");

아파치 코 몬즈 라이브러리는 이런 종류의 매우 유용합니다. 정규 표현식은 항상 성능면에서 비싸기 때문에이 특정 표현식은 정규 표현식보다 낫습니다.


답변

빠른 구현 : 활용 String.regionMatches()

정규 표현식을 사용하면 상대적으로 느릴 수 있습니다. 한 가지 경우 만 확인하려는 경우 (느리게) 중요하지 않습니다. 그러나 배열이나 수천 또는 수십만 개의 문자열 모음이 있으면 상황이 상당히 느려질 수 있습니다.

아래 제시된 솔루션은 정규 표현식을 사용하지 않으며 toLowerCase()(또한 다른 문자열을 만들고 검사 후에 버릴 수 있기 때문에 느립니다).

이 솔루션은 알 수없는 String.regionMatches () 메서드를 기반으로합니다 . 두 String영역이 일치 하는지 확인 하지만 중요한 것은 편리한 ignoreCase매개 변수 가있는 과부하가 있다는 것 입니다.

public static boolean containsIgnoreCase(String src, String what) {
    final int length = what.length();
    if (length == 0)
        return true; // Empty string is contained

    final char firstLo = Character.toLowerCase(what.charAt(0));
    final char firstUp = Character.toUpperCase(what.charAt(0));

    for (int i = src.length() - length; i >= 0; i--) {
        // Quick check before calling the more expensive regionMatches() method:
        final char ch = src.charAt(i);
        if (ch != firstLo && ch != firstUp)
            continue;

        if (src.regionMatches(true, i, what, 0, length))
            return true;
    }

    return false;
}

속도 분석

이 속도 분석은 로켓 과학이 아니라 다양한 방법이 얼마나 빠른지 대략적으로 보여줍니다.

5 가지 방법을 비교합니다.

  1. 우리 containsIgnoreCase () 방법.
  2. 두 문자열을 모두 소문자로 변환하고를 호출 String.contains()합니다.
  3. 소스 문자열을 소문자로 변환 String.contains()하고 미리 캐시 된 소문자 하위 문자열로 호출 합니다. 이 솔루션은 사전 정의 된 하위 문자열을 테스트하기 때문에 아직 유연하지 않습니다.
  4. 정규 표현식 사용 (허용 된 답변 Pattern.compile().matcher().find()…)
  5. 정규 표현식하지만 사전이 생성되고 캐시로 사용 Pattern. 이 솔루션은 미리 정의 된 하위 문자열을 테스트하기 때문에 아직 유연하지 않습니다.

결과 (메소드를 1,000 만 회 호출) :

  1. 우리의 방법 : 670ms
  2. 2 배 toLowerCase () 및 contains () : 2829ms
  3. 1x toLowerCase () 및 캐시 된 서브 스트링이있는 contains () : 2446ms
  4. 정규식 : 7180ms
  5. 캐시 된 정규 표현식 Pattern: 1845ms

테이블 결과 :

                                            RELATIVE SPEED   1/RELATIVE SPEED
 METHOD                          EXEC TIME    TO SLOWEST      TO FASTEST (#1)
------------------------------------------------------------------------------
 1. Using regionMatches()          670 ms       10.7x            1.0x
 2. 2x lowercase+contains         2829 ms        2.5x            4.2x
 3. 1x lowercase+contains cache   2446 ms        2.9x            3.7x
 4. Regexp                        7180 ms        1.0x           10.7x
 5. Regexp+cached pattern         1845 ms        3.9x            2.8x

우리의 방법은 소문자 및 사용에 비해 4 배 빠르며contains() , 정규 표현식을 사용할 때보 다 10 배 빠르며 사전 캐시 된 경우 에도 3 배 빠릅니다Pattern (임의의 하위 문자열을 확인하는 유연성이 손실 됨).


분석 테스트 코드

분석 수행 방법에 관심이 있으시면 다음과 같이 실행 가능한 완전한 응용 프로그램이 있습니다.

import java.util.regex.Pattern;

public class ContainsAnalysis {

    // Case 1 utilizing String.regionMatches()
    public static boolean containsIgnoreCase(String src, String what) {
        final int length = what.length();
        if (length == 0)
            return true; // Empty string is contained

        final char firstLo = Character.toLowerCase(what.charAt(0));
        final char firstUp = Character.toUpperCase(what.charAt(0));

        for (int i = src.length() - length; i >= 0; i--) {
            // Quick check before calling the more expensive regionMatches()
            // method:
            final char ch = src.charAt(i);
            if (ch != firstLo && ch != firstUp)
                continue;

            if (src.regionMatches(true, i, what, 0, length))
                return true;
        }

        return false;
    }

    // Case 2 with 2x toLowerCase() and contains()
    public static boolean containsConverting(String src, String what) {
        return src.toLowerCase().contains(what.toLowerCase());
    }

    // The cached substring for case 3
    private static final String S = "i am".toLowerCase();

    // Case 3 with pre-cached substring and 1x toLowerCase() and contains()
    public static boolean containsConverting(String src) {
        return src.toLowerCase().contains(S);
    }

    // Case 4 with regexp
    public static boolean containsIgnoreCaseRegexp(String src, String what) {
        return Pattern.compile(Pattern.quote(what), Pattern.CASE_INSENSITIVE)
                    .matcher(src).find();
    }

    // The cached pattern for case 5
    private static final Pattern P = Pattern.compile(
            Pattern.quote("i am"), Pattern.CASE_INSENSITIVE);

    // Case 5 with pre-cached Pattern
    public static boolean containsIgnoreCaseRegexp(String src) {
        return P.matcher(src).find();
    }

    // Main method: perfroms speed analysis on different contains methods
    // (case ignored)
    public static void main(String[] args) throws Exception {
        final String src = "Hi, I am Adam";
        final String what = "i am";

        long start, end;
        final int N = 10_000_000;

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCase(src, what);
        end = System.nanoTime();
        System.out.println("Case 1 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsConverting(src, what);
        end = System.nanoTime();
        System.out.println("Case 2 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsConverting(src);
        end = System.nanoTime();
        System.out.println("Case 3 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCaseRegexp(src, what);
        end = System.nanoTime();
        System.out.println("Case 4 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCaseRegexp(src);
        end = System.nanoTime();
        System.out.println("Case 5 took " + ((end - start) / 1000000) + "ms");
    }

}


답변

이 작업을 수행하는 간단한 방법 (패턴 일치에 대한 걱정없이)은 Strings를 소문자 로 변환하는 것입니다 .

String foobar = "fooBar";
String bar = "FOO";
if (foobar.toLowerCase().contains(bar.toLowerCase()) {
    System.out.println("It's a match!");
}


답변

그렇습니다.

String s1 = "abBaCca";
String s2 = "bac";

String s1Lower = s1;

//s1Lower is exact same string, now convert it to lowercase, I left the s1 intact for print purposes if needed

s1Lower = s1Lower.toLowerCase();

String trueStatement = "FALSE!";
if (s1Lower.contains(s2)) {

    //THIS statement will be TRUE
    trueStatement = "TRUE!"
}

return trueStatement;

이 코드는 문자열 “TRUE!”를 반환합니다. 당신의 캐릭터가 포함되어 있음을 발견했습니다.


답변

정규 표현식을 사용할 수 있으며 작동합니다.

boolean found = s1.matches("(?i).*" + s2+ ".*");