대소 문자를 구분하지 않는 목록 검색


144

testList많은 문자열을 포함 하는 목록 이 있습니다. testList목록에없는 경우에만 새 문자열을 추가하고 싶습니다 . 따라서 대소 문자를 구분하지 않고 목록을 검색하고 효율적으로 만들어야합니다. Contains케이스를 고려하지 않기 때문에 사용할 수 없습니다 . 또한 ToUpper/ToLower성능상의 이유로 사용하고 싶지 않습니다 . 나는이 방법을 발견했다.

    if(testList.FindAll(x => x.IndexOf(keyword, 
                       StringComparison.OrdinalIgnoreCase) >= 0).Count > 0)
       Console.WriteLine("Found in list");

이것은 작동하지만 부분 단어와도 일치합니다. 목록에 "염소"가 포함되어 있으면 "귀리"가 이미 목록에 있다고 주장하기 때문에 "귀리"를 추가 할 수 없습니다. 단어가 정확히 일치해야하는 대소 문자를 구분하지 않는 방식으로 목록을 효율적으로 검색하는 방법이 있습니까? 감사

답변:


180

String.IndexOf 대신 String.Equals 를 사용 하여 부분 일치가 없는지 확인하십시오. 또한 모든 요소를 ​​통과하는 FindAll을 사용하지 말고 FindIndex를 사용 하십시오 (첫 번째 요소는 중지합니다).

if(testList.FindIndex(x => x.Equals(keyword,  
    StringComparison.OrdinalIgnoreCase) ) != -1) 
    Console.WriteLine("Found in list"); 

또는 일부 LINQ 방법을 사용하십시오 (첫 번째 방법에서도 중지됨)

if( testList.Any( s => s.Equals(keyword, StringComparison.OrdinalIgnoreCase) ) )
    Console.WriteLine("found in list");

간단히 말해 몇 가지 빠른 테스트에서 첫 번째 방법은 약 50 % 더 빠릅니다. 다른 사람이 그것을 확인 / 거부 할 수 있습니다.
Brap

8
.NET 2.0부터는 쉽게 수행 할 수 있습니다. 아래 shaxby의 답변을보십시오.
Joe

3
Contains 메소드 shaxby의 참조 (IEqualityComparer를 사용하는 과부하가 있음)는 LINQ의 일부이므로 .NET 2.0 이후에는 사용할 수 없었습니다. StringComparer 클래스는 잠시 동안 사용되었습니다. List <T>에는 해당 메소드가 없으며 ArrayList 또는 StringCollection ( '자신의'목록 '으로 쉽게 참조 할 수있는 것)도 없습니다.
Adam Sills 2016 년

글쎄, 실제로 색인이 필요 했기 때문에 이것은 나에게 가장 좋은 대답이었습니다.
Nyerguds

1
첫 번째 솔루션은 List<>.Exists(Predicate<>)인스턴스 메소드를 사용해야 합니다. 또한 목록에 null항목이 포함되어 있으면 문제가 발생할 수 있습니다. 이 경우 ( null을 절대로 보장 하지 않는 경우) keyword.Equals(x, StringComparison.OrdinalIgnoreCase)보다 말하는 것이 더 안전합니다 . x.Equals(keyword, StringComparison.OrdinalIgnoreCase)keyword
Jeppe Stig Nielsen

360

나는 이것이 오래된 게시물이라는 것을 알고 있지만 다른 사람이 찾고 있는 경우를 대비하여 대소 문자를 구분하지 않는 문자열 동등 비교기를 제공하여 사용할 있습니다 Contains.

using System.Linq;

// ...

if (testList.Contains(keyword, StringComparer.OrdinalIgnoreCase))
{
    Console.WriteLine("Keyword Exists");
}

이것은 msdn 에 따라 .net 2.0부터 사용 가능 합니다.


21
확실히 최고의 답변입니다. :)
Joe

22
Enumerable <T>. .NET 2.0 이후에는 (참조하는 내용)이 포함되어 있지 않습니다. 사용중인 과부하가있는 List <T>가 없습니다.
Adam Sills

@AdamSills 맞습니다. List <T>에는 그러한 포함 메소드가 없습니다. 그리고 게으른 컬렉션이라면 다른 Enumerable <T> 메서드처럼 몇 번 반복 할 수 있습니다. Imho,이 방법은 그 경우에는 논리적이지 않기 때문에 그러한 경우에는 사용해서는 안됩니다.
세르게이 Litvinov

40
처음에는이 과부하를 보지 못했지만 System.Linq를 사용하여 추가해야합니다.
Michael

1
StringComparer클래스는 2.0 이후 주변왔다,하지만 포함의 과부하은 3.5에서 소개되었습니다. msdn.microsoft.com/ko-kr/library/bb339118(v=vs.110).aspx
Denise Skidmore

18

위의 Adam Sills 답변을 기반으로-Contains ... :)

///----------------------------------------------------------------------
/// <summary>
/// Determines whether the specified list contains the matching string value
/// </summary>
/// <param name="list">The list.</param>
/// <param name="value">The value to match.</param>
/// <param name="ignoreCase">if set to <c>true</c> the case is ignored.</param>
/// <returns>
///   <c>true</c> if the specified list contais the matching string; otherwise, <c>false</c>.
/// </returns>
///----------------------------------------------------------------------
public static bool Contains(this List<string> list, string value, bool ignoreCase = false)
{
    return ignoreCase ?
        list.Any(s => s.Equals(value, StringComparison.OrdinalIgnoreCase)) :
        list.Contains(value);
}

10

StringComparer를 사용할 수 있습니다 :

    var list = new List<string>();
    list.Add("cat");
    list.Add("dog");
    list.Add("moth");

    if (list.Contains("MOTH", StringComparer.OrdinalIgnoreCase))
    {
        Console.WriteLine("found");
    }

1
"System.Linq 사용"을 추가하지 않으면 .Contains에 대한 과부하가 표시되지 않습니다.
Julian Melville

1

랜스 라슨 (Lance Larsen) 답변을 기반으로-권장 문자열이있는 확장 방법이 있습니다. 문자열 대신 비교하십시오.

StringComparison 매개 변수를 사용하는 String.Compare의 오버로드를 사용하는 것이 좋습니다. 이러한 오버로드를 통해 의도 한 정확한 비교 동작을 정의 할 수있을뿐만 아니라이를 사용하면 다른 개발자가 코드를보다 쉽게 ​​읽을 수 있습니다. [ 조쉬 프리 @ BCL 팀 블로그 ]

public static bool Contains(this List<string> source, string toCheck, StringComparison comp)
{
    return
       source != null &&
       !string.IsNullOrEmpty(toCheck) &&
       source.Any(x => string.Compare(x, toCheck, comp) == 0);
}

0

IndexOf의 결과가 0보다 크거나 같은지 여부를 확인 중 입니다. 즉, 문자열의 어느 곳에서나 일치가 시작되는지 여부를 의미합니다 . 0 과 같은지 확인하십시오 .

if (testList.FindAll(x => x.IndexOf(keyword, 
                   StringComparison.OrdinalIgnoreCase) >= 0).Count > 0)
   Console.WriteLine("Found in list");

이제 "염소"와 "귀리"는 일치하지 않지만 "염소"와 "고아"는 일치합니다. 이를 피하기 위해 두 줄의 길이를 비교할 수 있습니다.

이 모든 합병증을 피하기 위해 목록 대신 사전을 사용할 수 있습니다. 키는 소문자 문자열이고 값은 실제 문자열입니다. 이렇게하면 ToLower각 비교 에 사용할 필요 가 없기 때문에 성능이 저하되지 않지만 여전히을 사용할 수 있습니다 Contains.


0

아래는 전체 목록에서 키워드를 검색하고 해당 항목을 제거하는 예입니다.

public class Book
{
  public int BookId { get; set; }
  public DateTime CreatedDate { get; set; }
  public string Text { get; set; }
  public string Autor { get; set; }
  public string Source { get; set; }
}

Text 속성에 키워드가 포함 된 책을 제거하려면 키워드 목록을 만들어 책 목록에서 제거 할 수 있습니다.

List<Book> listToSearch = new List<Book>()
   {
        new Book(){
            BookId = 1,
            CreatedDate = new DateTime(2014, 5, 27),
            Text = " test voprivreda...",
            Autor = "abc",
            Source = "SSSS"

        },
        new Book(){
            BookId = 2,
            CreatedDate = new DateTime(2014, 5, 27),
            Text = "here you go...",
            Autor = "bcd",
            Source = "SSSS"


        }
    };

var blackList = new List<string>()
            {
                "test", "b"
            }; 

foreach (var itemtoremove in blackList)
    {
        listToSearch.RemoveAll(p => p.Source.ToLower().Contains(itemtoremove.ToLower()) || p.Source.ToLower().Contains(itemtoremove.ToLower()));
    }


return listToSearch.ToList();

-1

비슷한 문제가 있었지만 항목의 색인이 필요했지만 대소 문자를 구분하지 않아야했습니다. 몇 분 동안 웹을 둘러 보았고 아무것도 발견하지 못했습니다. 그래서 작은 방법을 작성하여 완료했습니다. 했다 :

private static int getCaseInvariantIndex(List<string> ItemsList, string searchItem)
{
    List<string> lowercaselist = new List<string>();

    foreach (string item in ItemsList)
    {
        lowercaselist.Add(item.ToLower());
    }

    return lowercaselist.IndexOf(searchItem.ToLower());
}

이 코드를 동일한 파일에 추가하고 다음과 같이 호출하십시오.

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

이것이 도움이되기를 바랍니다. 행운을 빕니다!


1
왜 두 번째 목록을 생성합니까? 그다지 효율적이지 않습니다. for (var i = 0; i <itemsList.Count; i ++) {if (item.ToLower () == searchItem.ToLower ()) {return i}}
wesm

나는 우리가 결코 알지 못할 것 같아요.
Denny
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.