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


386

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

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

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

return s1.contains(s2);

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

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

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


DrJava 는 문서가 실패했을 때이를 테스트하는 매우 쉬운 방법입니다. Interactions 창에 몇 가지 테스트 사례를 입력하면 알 수 있습니다.
EfForEffort

17
나는 당신이 당신의 자신의 질문에 대답했다고 생각합니다. 아래 솔루션 중 어느 것이 이것보다 낫다고 생각하지 않습니다. 그러나 그들은 확실히 느립니다.
Nikolay Dimitrov

7
귀하의 솔루션은 답변에있는 것보다 훨씬 간단합니다
LobsterMan

2
여기에 나와있는 많은 사람들이 찾고있는 대답은 귀하의 질문에 있습니다.
Lalit Fauzdar

1
귀하의 예는 가장 간단하고 읽기 쉽고 아마도이 작업을 수행하는 가장 좋은 방법입니다-내가보고있는 답변보다 낫습니다.
user1258361

답변:


320

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

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

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


23
에 대한 문서에서 언급했듯이 Pattern.CASE_INSENSITIVEASCII 문자에 대해서만 작동합니다 (예 : "Ä"는 "ä"와 일치하지 않습니다). UNICODE_CASE이를 달성 하려면 플래그 를 추가로 지정해야합니다 .
Philipp Wendler

72
이 방법은 Pattern보다 성능이 뛰어나 s1.toLowerCase().contains(s2.toLowerCase())있습니까?
Rajat Gupta

6
@ user01 속도 분석을 수행했습니다. 결과에 대한 내 답변보기 (더 빠른 솔루션도 보여주었습니다) : stackoverflow.com/a/25379180/1705598
icza

10
: 우리가 더 나은 변수 이름이 있다면 그것은 내게 무슨 일했다가 지워 더 많은 것Pattern.compile(Pattern.quote(needle), Pattern.CASE_INSENSITIVE).matcher(haystack).find()
존 바워스를

5
@ user01의 정확성은 성능보다 우선하며 toLowerCase를 사용하면 잠재적으로 잘못된 결과를 얻을 수 있습니다 (예 : 동일한 대문자 양식에 대해 두 개의 소문자 형식이있는 Sigma 문자가 포함 된 특정 그리스어 텍스트를 비교할 때).
Klitos Kyriacou

266

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

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

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

1
좋은 캐치 매트. 소문자 또는 패턴 솔루션이 더 효율적인 방법을 알고 싶습니다. 단일 비교에는 패턴을 덜 효율적으로 사용하지 않지만 다중 비교에는 더 효율적입니까?
Aaron

41
아마도 대부분의 경우 .toLowerCase (). contains () 메소드가 더 빠를 것입니다. 복잡성을 줄이기 위해 해당 스타일을 선호 할 것입니다.
매트 퀘일

3
@AaronFerguson 예, 실제로 toLowerCase().contains()더 빠릅니다. 나는 약간의 속도 분석을 수행 내 결과에 대한 대답을 참조하십시오 stackoverflow.com/a/25379180/1705598
icza

2
@MattQuail 정확하지 않은 경우 더 빠르다는 점은 없습니다. 예를 들어, 그리스어 대문자 시그마는 두 단어의 소문자 형식 (단어 끝에 나오는지 여부에 따라 다름)에 따라 대소 문자를 구분하지 않는 하위 문자열 일치를 시도 할 때 하위 문자열이 시그마로 끝나는 경우 쉽게 잘못 될 수 있습니다. 결과.
Klitos Kyriacou

Pattern.UNICODE_CASE플래그도 추가해야한다고 생각합니다 . 이것을 확인해 주시겠습니까?
Thariq Nugrohotomo

160

당신이 사용할 수있는

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

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


1
이것이 로케일을 존중하는지 아는 사람이 있습니까?
Charles Wood

12
@CharlesWood String.regionMatches문자 단위 변환을 사용하는에 위임합니다 . 또한 containsIgnoreCase("ß", "ss")모든 로케일에서 잘못된 -1을 리턴합니다 (독일 "sharp s"는 "ss"로 대문자로
표시됨)

그렇다면 독일어 단어를 비교하는 올바른 방법은 무엇입니까? 그것은 문자열을 비교하는 모든 방법을 복잡하게 만드는 하나의 언어 인 것 같습니다 : P
chomp

1
BTW : 독일어는 2017 년에 공식적으로 대문자 ß로 확장되었습니다 : de.wikipedia.org/wiki/Gro%C3%9Fes_%C3%9F . 독일어 키보드에 입력 Shift + Alt를 할머니 + ß -> 검사 : ẞ 😁
Kawu

119

빠른 구현 : 활용 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");
    }

}

6
+1이지만 ß(German sharp S; 대문자로 SS) 및 일부 다른 문자에 대해서는 실패합니다 ( 의 소스 참조, String.regionMatches두 가지 변환을 시도).
maaartinus

2
당신은 항상 같은 문자열을 테스트하는데, 이것은 실제로 공정한 비교가 아닙니다. 'i am'은 항상 중간에 있으며 다른 검색 방법에 따라 차이가있을 수 있습니다. 임의의 문자열을 생성하고 하위 문자열이없는 경우 속도를보고하는 것이 좋습니다.

2
그건 정말 가까이 아파치 StringUtils에 방법에 보인다 grepcode.com/file/repo1.maven.org/maven2/org.apache.commons/...
alain.janinm

1
@ alain.janinm 비슷한 점을 보지 못했습니다. "가까운"것으로 보이는 유일한 것은 StringUtils.containsIgnoreCase()내 솔루션과 Apache 솔루션이 regionMatches()(주기마다) 메소드를 사용 하지만 심지어 내가 호출하는 것과 String.regionMatches()Apache 호출과 동일하지 않다는 것 CharSequenceUtils.regionMatches()입니다.
icza

2
@icza CharSequenceUtils.regionMatchesString.regionMatches실제로 호출합니다 . 어쨌든 내 요점은 정보를 제공하는 것이 었습니다. 누군가 이미 StringUtils lib를 사용하고 있다면 벤치 마크로 증명하는 것처럼 효율적인 방법 인 것처럼 보일 수 있습니다. 나는 아파치 lib 디렉토리를 사용하지 않은 경우, 나는 확실히 당신의 방법을 사용할 것)
alain.janinm

22

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

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

4
대소 문자는 언어에 따라 다르므로 컴퓨터에서는 작동하지만 고객에게는 실패합니다. :) @Adriaan Koster 의견을 참조하십시오.
kroiz

1
@kroiz는 String의 출처에 따라 다릅니다. "foobar"와 "FOO"를 비교하면 항상 일치하지만 사용자 입력 정보 또는 언어 별 컨텐츠를 비교하는 경우에는 옳습니다. 개발자는주의해야합니다.
Phil

16

그렇습니다.

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!"를 반환합니다. 당신의 캐릭터가 포함되어 있음을 발견했습니다.


12
toLowerCase () 사용의 큰 단점은 결과가 현재 로케일에 의존한다는 것입니다. 참조 : javapapers.com/core-java/…
Adriaan Koster

4
이 질문은 소문자가 아닌 경우 실패하므로 실제로 더 나은 솔루션이 포함되어 있습니다 s2. 이러한 세부 사항에 대해서는 말하지 않고 컴파일되지 않으며 문자열을 반환합니다.
maaartinus


3

ICU4j를 사용하면 유니 코드 친화적 인 것들이 있습니다. 기본 강도 비교는 대소 문자를 무시하지만 로케일에 따라 달라지는 특성으로 설명되기 때문에 메소드 이름에 "대소 문자 무시"가 의심 스럽다고 생각합니다. 그러나 사용자가 기대하는 방식으로 로케일에 따라 다릅니다.

public static boolean containsIgnoreCase(String haystack, String needle) {
    return indexOfIgnoreCase(haystack, needle) >= 0;
}

public static int indexOfIgnoreCase(String haystack, String needle) {
    StringSearch stringSearch = new StringSearch(needle, haystack);
    stringSearch.getCollator().setStrength(Collator.PRIMARY);
    return stringSearch.first();
}

3

대소 문자를 구분하지 않는 문자열을 찾는 테스트를 수행했습니다. String을 하나의 필드로 사용하여 150,000 개의 객체로 구성된 Vector가 있고 문자열과 일치하는 부분 집합을 찾고 싶습니다. 세 가지 방법을 시도했습니다.

  1. 모두 소문자로 변환

    for (SongInformation song: songs) {
        if (song.artist.toLowerCase().indexOf(pattern.toLowercase() > -1) {
                ...
        }
    }
  2. String matches () 메소드 사용

    for (SongInformation song: songs) {
        if (song.artist.matches("(?i).*" + pattern + ".*")) {
        ...
        }
    }
  3. 정규식 사용

    Pattern p = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE);
    Matcher m = p.matcher("");
    for (SongInformation song: songs) {
        m.reset(song.artist);
        if (m.find()) {
        ...
        }
    }

타이밍 결과는 다음과 같습니다.

  • 일치하지 않는 항목 : 20msec

  • 일치를 낮추려면 : 182msec

  • 문자열 일치 : 278msec

  • 정규식 : 65msec

정규식은이 사용 사례에서 가장 빠른 것으로 보입니다.


타이밍 결과를 넣는 것이 좋습니다. 누구나 정규 표현식이 얼마나 느린 지에 대해 말하지만 실제로 정규 표현식을 한 번만 컴파일하면 매우 빠릅니다.
woot

1

정규식 플래그 (대소 문자를 구분하지 않는 {i})를 사용하는 간단한 방법이 있습니다.

 String s1 = "hello abc efg";
 String s2 = "ABC";
 s1.matches(".*(?i)"+s2+".*");

/*
 * .*  denotes every character except line break
 * (?i) denotes case insensitivity flag enabled for s2 (String)
 * */

0

주요 질문이 무엇인지 잘 모르겠지만 .contains는 대소 문자를 구분합니다.


0
String container = " Case SeNsitive ";
String sub = "sen";
if (rcontains(container, sub)) {
    System.out.println("no case");
}

public static Boolean rcontains(String container, String sub) {

    Boolean b = false;
    for (int a = 0; a < container.length() - sub.length() + 1; a++) {
        //System.out.println(sub + " to " + container.substring(a, a+sub.length()));
        if (sub.equalsIgnoreCase(container.substring(a, a + sub.length()))) {
            b = true;
        }
    }
    return b;
}

기본적으로 두 개의 문자열을 사용하는 방법입니다. contains ()의 대소 문자를 구분하지 않는 버전이어야합니다. contains 메소드를 사용할 때 한 문자열이 다른 문자열에 포함되어 있는지 확인하려고합니다.

이 메소드는 "sub"인 문자열을 가져 와서 "sub"와 길이가 같은 컨테이너 문자열의 서브 스트링과 같은지 확인합니다. 당신이 보면for루프 컨테이너 문자열에서 하위 문자열 ( "sub"의 길이)에서 반복되는 것을 볼 수 있습니다.

각 반복은 컨테이너 문자열 equalsIgnoreCase의 하위 문자열이 하위 문자열인지 확인합니다 .


기본적으로 두 문자열을 취하는 방법입니다. contains ()의 대소 문자를 구분하지 않는 버전이라고 가정합니다. contains 메소드를 사용할 때 한 문자열이 다른 문자열에 포함되어 있는지 확인하려고합니다. 이 메소드는 "sub"인 문자열을 가져 와서 컨테이너 문자열의 서브 문자열과 같은지, 길이가 "sub"와 같은지 확인합니다. for 루프를 보면 컨테이너 문자열에서 하위 문자열 ( "sub"의 길이)에서 반복되는 것을 볼 수 있습니다. 각 반복은 컨테이너 문자열의 하위 문자열이 하위와 동일한 부호인지 확인합니다.
seth

@ 아마도 대답에 추가해야합니다.
모자와 사람

2
이것은 가장 느린 방법이며 ... 독일에게는 실패합니다.
maaartinus

0

URL 과 같은 다른 ASCII 문자열에서 ASCII 문자열을 검색 해야하는 경우 내 솔루션이 더 좋습니다. 나는 icza의 방법과 내 속도를 테스트했으며 결과는 다음과 같습니다.

  • 사례 1에 2788ms가 걸렸습니다-regionMatches
  • 사례 2는 1520ms 걸렸습니다-나의

코드:

public static String lowerCaseAscii(String s) {
    if (s == null)
        return null;

    int len = s.length();
    char[] buf = new char[len];
    s.getChars(0, len, buf, 0);
    for (int i=0; i<len; i++) {
        if (buf[i] >= 'A' && buf[i] <= 'Z')
            buf[i] += 0x20;
    }

    return new String(buf);
}

public static boolean containsIgnoreCaseAscii(String str, String searchStr) {
    return StringUtils.contains(lowerCaseAscii(str), lowerCaseAscii(searchStr));
}

0
import java.text.Normalizer;

import org.apache.commons.lang3.StringUtils;

public class ContainsIgnoreCase {

    public static void main(String[] args) {

        String in = "   Annulée ";
        String key = "annulee";

        // 100% java
        if (Normalizer.normalize(in, Normalizer.Form.NFD).replaceAll("[\\p{InCombiningDiacriticalMarks}]", "").toLowerCase().contains(key)) {
            System.out.println("OK");
        } else {
            System.out.println("KO");
        }

        // use commons.lang lib
        if (StringUtils.containsIgnoreCase(Normalizer.normalize(in, Normalizer.Form.NFD).replaceAll("[\\p{InCombiningDiacriticalMarks}]", ""), key)) {
            System.out.println("OK");
        } else {
            System.out.println("KO");
        }

    }

}

이 코드 스 니펫에 대해 약간의 단기적인 도움을 제공 할 수 있습니다. 적절한 설명 이것이 문제에 대한 좋은 해결책 인지 보여줌으로써 장기적인 가치를 크게 향상시킬 것이며, 다른 비슷한 질문을 가진 미래 독자들에게 더 유용 할 것입니다. 제발 편집 당신이 만든 가정 등 일부 설명을 추가 할 답변을.
Toby Speight

0
"AbCd".toLowerCase().contains("abcD".toLowerCase())

2
코드로 문제를 해결하는 방법을 설명하여 답변을 향상시킬 수 있습니까?
Isuka

1
이 답변은 다른 사람들이 제공 한이 질문에 대한 다른 더 자세한 답변에서 이미 제안되었습니다. 나는이 답변이 어떤 목적에도 도움이되지 않는다고 생각한다.
DaveyDaveDave

0

우리는 anyMatch와 함께 스트림을 사용할 수 있으며 Java 8을 포함합니다.

public class Test2 {
    public static void main(String[] args) {

        String a = "Gina Gini Protijayi Soudipta";
        String b = "Gini";

        System.out.println(WordPresentOrNot(a, b));
    }// main

    private static boolean WordPresentOrNot(String a, String b) {
    //contains is case sensitive. That's why change it to upper or lower case. Then check
        // Here we are using stream with anyMatch
        boolean match = Arrays.stream(a.toLowerCase().split(" ")).anyMatch(b.toLowerCase()::contains);
        return match;
    }

}

0

또는 간단한 접근 방식을 사용하고 문자열의 대소 문자를 하위 문자열의 대소 문자로 변환 한 다음 contains 메소드를 사용할 수 있습니다.


-1
String x="abCd";
System.out.println(Pattern.compile("c",Pattern.CASE_INSENSITIVE).matcher(x).find());

-1

당신은 단순히 다음과 같이 할 수 있습니다 :

String s1 = "AbBaCca";
String s2 = "bac";
String toLower = s1.toLowerCase();
return toLower.contains(s2);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.