문자열에 목록의 요소가 포함되어 있는지 확인하십시오 (문자열).


155

다음 코드 블록의 경우 :

For I = 0 To listOfStrings.Count - 1
    If myString.Contains(lstOfStrings.Item(I)) Then
        Return True
    End If
Next
Return False

출력은 다음과 같습니다.

사례 1 :

myString: C:\Files\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: True

사례 2 :

myString: C:\Files3\myfile.doc
listOfString: C:\Files\, C:\Files2\
Result: False

목록 (listOfStrings)에는 여러 항목 (최소 20 개)이 포함될 수 있으며 myString과 같은 수천 개의 문자열에 대해 점검해야합니다.

이 코드를 작성하는 더 나은 (더 효율적인) 방법이 있습니까?

답변:


359

LINQ를 사용하고 C #을 사용합니다 (요즘 VB를 많이 알지 못합니다).

bool b = listOfStrings.Any(s=>myString.Contains(s));

또는 (짧고 효율적이지만 분명하지는 않다) :

bool b = listOfStrings.Any(myString.Contains);

평등을 테스트하는 경우 HashSet등을 살펴볼 가치가 있지만 부분 일치로 조각을 나누고 복잡한 순서를 추가하지 않으면 부분 일치에 도움이되지 않습니다.


업데이트 : 정말로 "StartsWith"를 의미한다면, 목록을 정렬하여 배열에 넣을 수 있습니다. 그런 다음을 사용 Array.BinarySearch하여 각 항목을 찾으십시오. 전체 또는 부분 일치인지 확인하여 조회하십시오.


1
포함 대신에 그의 예제를 기반으로 StartsWith를 사용합니다.
tvanfosson

@tvanfosson-예제가 완전히 포함되는지 여부에 따라 다르지만 예, 동의합니다. 물론 간단하게 변경할 수 있습니다.
Marc Gravell

이 코드는 알고리즘 수준에서 얼마나 더 효율적입니까? "Any"의 루프가 더 빠르면 더 짧고 빠릅니다. 그러나 여러 번 정확하게 일치해야하는 문제는 동일합니다.
Torsten Marek

세트를 사용하는 경우 사용자 정의 비교기를 설정할 수 있습니다.
Fortyrunner

두 번째는 실제로 측정 가능한 차이로 인해 더 효율적이지 않습니다.
ICR

7

문자열을 만들 때 다음과 같아야합니다

bool inact = new string[] { "SUSPENDARE", "DIZOLVARE" }.Any(s=>stare.Contains(s));

5

이전의 비슷한 질문 " 대규모 목록과 비교하여 기존 문자열을 테스트하는 가장 좋은 방법 "의 여러 제안이있었습니다 .

정규식이 요구 사항에 충분할 수 있습니다. 표현식은 모든 후보 서브 스트링을 연결하고 OR " |"연산자를 사용합니다. 물론 표현식을 만들 때 이스케이프 처리되지 않은 문자를 보거나 복잡하거나 크기 제한으로 인해 컴파일에 실패해야합니다.

이 작업을 수행하는 또 다른 방법 은 모든 후보 하위 문자열을 나타내는 trie 데이터 구조 를 구성하는 것입니다 (정규 일치자가 수행하는 작업이 다소 중복 될 수 있음). 테스트 문자열에서 각 문자를 단계별로 살펴보면 trie의 루트에 대한 새 포인터를 만들고 기존 포인터를 해당 자식 (있는 경우)으로 이동시킵니다. 포인터가 잎에 도달하면 일치합니다.


5

Marc의 답변이 마음에 들었지만 CaSe InSenSiTiVe가되기 위해서는 Contains 일치가 필요했습니다.

이것이 해결책이었습니다.

bool b = listOfStrings.Any(s => myString.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0))

> -1이 아니어야합니까?
CSharped

1
@CSharped> -1 (빼기 1 이상)과> = 0 (0 이상)은 동일하므로 중요하지 않습니다.
WhoIsRich

2

패턴에 따라 포함 대신 StartsWith를 사용하도록 변경하는 것이 개선되었습니다. 모든 문자 위치를 찾을 때마다 검색을 다시 시작하지 않고 첫 번째 불일치를 찾을 때까지 각 문자열을 반복해야합니다.

또한 패턴을 기반으로 myString 경로의 첫 번째 부분을 추출한 다음 비교를 반대로하여 다른 방법이 아닌 문자열 목록에서 myString의 시작 경로를 찾는 것처럼 보일 수 있습니다.

string[] pathComponents = myString.Split( Path.DirectorySeparatorChar );
string startPath = pathComponents[0] + Path.DirectorySeparatorChar;

return listOfStrings.Contains( startPath );

편집 : 이것은 변경할 수 있기 때문에 @Marc Gravell 언급 HashSet의 아이디어를 사용하여 더 빠른 것 Contains으로 ContainsKey하고 조회가 O (1) 대신 O (N)이 될 것입니다. 경로가 정확히 일치하는지 확인해야합니다. 이것은 @Marc Gravell의 일반적인 솔루션은 아니지만 예제에 맞게 조정되었습니다.

C # 예제가 유감입니다. VB로 번역 할 커피가 충분하지 않았습니다.


다시 시작; 이진 검색을 미리 정렬하고 사용합니까? 다시 더 빠를 수도 있습니다.
Marc Gravell

2

오래된 질문입니다. 그러나 그 이후 VB.NET로 원래 요구 사항이있었습니다. 허용 된 답변과 동일한 값을 사용하십시오.

listOfStrings.Any(Function(s) myString.Contains(s))

1

속도를 테스트 했습니까?

즉, 샘플 데이터 세트를 작성하고 프로파일 링했습니까? 생각만큼 나쁘지 않을 수 있습니다.

이것은 또한 별도의 스레드로 생성되어 속도의 환상을 줄 수있는 것일 수도 있습니다!


0

속도가 중요한 경우 패턴 세트에 대한 Aho-Corasick 알고리즘 을 찾아 볼 수 있습니다 .

그것은 실패 링크 가있는 trie 입니다. 즉, 복잡성은 O (n + m + k)입니다. 여기서 n은 입력 텍스트의 길이, m은 패턴의 누적 길이 및 k는 일치 횟수입니다. 첫 번째 일치 항목을 찾은 후 종료되도록 알고리즘을 수정해야합니다.



0

Contains방법 의 단점은 문자열을 비교할 때 종종 중요한 비교 유형을 지정할 수 없다는 것입니다. 항상 문화권과 대소 문자를 구분합니다. WhoIsRich의 답변이 가치 있다고 생각합니다. 더 간단한 대안을 보여주고 싶습니다.

listOfStrings.Any(s => s.Equals(myString, StringComparison.OrdinalIgnoreCase))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.