문자열에서 N 번째 문자 찾기


83

문자열에서 N 번째 문자의 인덱스를 반환하는 C # 메서드를 만드는 데 도움이 필요합니다.

예를 들어, 't'문자열에서 세 번째로 나오는 문자 "dtststxtu"는 5입니다
(문자열에는 4 t초가 있습니다.)


지금까지 무엇을해야합니까?
Anthony Forloney 2010

3
나는 당신이 원하는 것을 더 명확하게 전달하기 위해 당신의 대답을 편집했습니다. 질문에 맞는 답을 얻을 수 있기를 바랍니다. 영어에 능통하지 않다는 것은 Stack Overflow에서 문제가되지 않습니다. 당신은 항상 유창한 사람에게 질문을 편집하고 정리하도록 요청하는 줄을 추가 할 수 있지만, 사람들이 무엇을 이해할 수 있도록 질문에 몇 가지 예를 제공하기 위해 노력해야합니다. 당신은 필요합니다.
Lasse V. Karlsen

답변:


94
public int GetNthIndex(string s, char t, int n)
{
    int count = 0;
    for (int i = 0; i < s.Length; i++)
    {
        if (s[i] == t)
        {
            count++;
            if (count == n)
            {
                return i;
            }
        }
    }
    return -1;
}

훨씬 더 깔끔하게 만들 수 있으며 입력에 대한 검사가 없습니다.


7
훌륭한 접근. 멋지고 깨끗하고 읽기 쉽고 유지 관리가 쉬우 며 성능이 뛰어납니다.
Mike

1
이와 같은 루프를 사랑합니다. 뛰어난 성능을 제공 할뿐만 아니라 모든 것이 눈앞에서 맑고 투명하기 때문에 잘못 갈 수 없습니다. 당신은 linq를 작성하고 일부 개발자는 비용을 이해하지 못하는 루프에 넣고 모두가 성능 병목 현상이 어디에 있는지 계속 궁금해합니다.
user734028

20

이전 솔루션에는 사소한 버그가 있습니다.

다음은 업데이트 된 코드입니다.

s.TakeWhile(c => (n -= (c == t ? 1 : 0)) > 0).Count();

1
캐릭터를 찾지 못하면 무엇을 반환합니까?
Timuçin 2014 년

문자열의 길이 / 개수를 반환합니다. 그 값을 확인해야합니다.
Yoky 2015

10

다른 LINQ 솔루션은 다음과 같습니다.

string input = "dtststx";
char searchChar = 't';
int occurrencePosition = 3; // third occurrence of the char
var result = input.Select((c, i) => new { Char = c, Index = i })
                  .Where(item => item.Char == searchChar)
                  .Skip(occurrencePosition - 1)
                  .FirstOrDefault();

if (result != null)
{
    Console.WriteLine("Position {0} of '{1}' occurs at index: {2}",
                        occurrencePosition, searchChar, result.Index);
}
else
{
    Console.WriteLine("Position {0} of '{1}' not found!",
                        occurrencePosition, searchChar);
}

재미로 여기 Regex 솔루션이 있습니다. 처음에 일부 사람들은 Regex를 사용하여 계산했지만 질문이 변경되었을 때 업데이트가 이루어지지 않았습니다. Regex로 할 수있는 방법은 다음과 같습니다. 단순함을 위해 전통적인 접근 방식이 가장 좋습니다.

string input = "dtststx";
char searchChar = 't';
int occurrencePosition = 3; // third occurrence of the char

Match match = Regex.Matches(input, Regex.Escape(searchChar.ToString()))
                   .Cast<Match>()
                   .Skip(occurrencePosition - 1)
                   .FirstOrDefault();

if (match != null)
    Console.WriteLine("Index: " + match.Index);
else
    Console.WriteLine("Match not found!");

9

다음은 프레임 워크 메소드의 형식을 모방 한 확장 메소드로서의 재귀 구현입니다.

public static int IndexOfNth(
    this string input, string value, int startIndex, int nth)
{
    if (nth < 1)
        throw new NotSupportedException("Param 'nth' must be greater than 0!");
    if (nth == 1)
        return input.IndexOf(value, startIndex);

    return input.IndexOfNth(value, input.IndexOf(value, startIndex) + 1, --nth);
}

또한 다음은 (정확함을 증명하기 위해) 도움이 될 수있는 (MBUnit) 단위 테스트입니다.

[Test]
public void TestIndexOfNthWorksForNth1()
{
    const string input = "foo<br />bar<br />baz<br />";
    Assert.AreEqual(3, input.IndexOfNth("<br />", 0, 1));
}

[Test]
public void TestIndexOfNthWorksForNth2()
{
    const string input = "foo<br />whatthedeuce<br />kthxbai<br />";
    Assert.AreEqual(21, input.IndexOfNth("<br />", 0, 2));
}

[Test]
public void TestIndexOfNthWorksForNth3()
{
    const string input = "foo<br />whatthedeuce<br />kthxbai<br />";
    Assert.AreEqual(34, input.IndexOfNth("<br />", 0, 3));
}

8

업데이트 : N 번째 발생 색인 한 줄 :

int NthOccurence(string s, char t, int n)
{
    s.TakeWhile(c => n - (c == t)?1:0 > 0).Count();
}

자신의 책임하에 사용하십시오. 이것은 숙제처럼 보이기 때문에 여러분이 찾을 수 있도록 몇 가지 버그를 남겨 두었습니다.

int CountChars(string s, char t)
{
   int count = 0;
   foreach (char c in s)
      if (s.Equals(t)) count ++;
   return count;
}

.

int CountChars(string s, char t)
{
     return s.Length - s.Replace(t.ToString(), "").Length;
}

.

int CountChars(string s, char t)
{
    Regex r = new Regex("[\\" + t + "]");
    return r.Match(s).Count;
}

4
n 값이 변경되지 않기 때문에 한 줄짜리 예제가 작동하지 않습니다.
Dave Neeley 2011 년

2
좋은 솔루션이지만, 변수가 람다의 범위를 벗어나 정의되어야하기 때문에 이것이 진정한 "한 줄짜리"는 아닙니다. s.TakeWhile (c => ((n-= (c == 't'))? 1 : 0)> 0) .Count ();
nullable

12
-1 "에 대한 그래서 내가 거기에 몇 가지 버그를 왼쪽하여 찾을 수 있습니다"
ZANON

6

ranomore는 Joel Coehoorn의 한 줄짜리가 작동하지 않는다고 올바르게 언급했습니다.

여기서 두 라이너 수행 업무, 문자의 출현 n 번째의 0 기반 인덱스를 반환 문자열 확장 방법 또는 -1 n 번째의 발생이없는 경우 :

public static class StringExtensions
{
    public static int NthIndexOf(this string s, char c, int n)
    {
        var takeCount = s.TakeWhile(x => (n -= (x == c ? 1 : 0)) > 0).Count();
        return takeCount == s.Length ? -1 : takeCount;
    }
}

4

Joel의 대답은 좋습니다 (그리고 나는 그것을 찬성했습니다). 다음은 LINQ 기반 솔루션입니다.

yourString.Where(c => c == 't').Count();

2
@Andrew- Where술어를 건너 뛰고 Count메소드에 전달하여 이를 단축 할 수 있습니다 . 그것이있는 방식에 문제가 있다는 것은 아닙니다.
Mike Two

10
이것은 n 번째 문자의 색인이 아니라 문자의 발생 횟수를 찾는 것뿐이 아닐까요?
dx_over_dt 2015-06-26

4

다른 방법에 비해 매우 빠르게 실행되는 또 다른 답변을 추가합니다.

private static int IndexOfNth(string str, char c, int nth, int startPosition = 0)
{
    int index = str.IndexOf(c, startPosition);
    if (index >= 0 && nth > 1)
    {
        return  IndexOfNth(str, c, nth - 1, index + 1);
    }

    return index;
}

3

재미있는 방법이 있습니다.

     int i = 0;
     string s="asdasdasd";
     int n = 3;
     s.Where(b => (b == 'd') && (i++ == n));
     return i;

3
public int GetNthOccurrenceOfChar(string s, char c, int occ)
{
    return String.Join(c.ToString(), s.Split(new char[] { c }, StringSplitOptions.None).Take(occ)).Length;
}

3
string result = "i am 'bansal.vks@gmail.com'"; // string

int in1 = result.IndexOf('\''); // get the index of first quote

int in2 = result.IndexOf('\'', in1 + 1); // get the index of second

string quoted_text = result.Substring(in1 + 1, in2 - in1); // get the string between quotes

2

정규식으로이 작업을 수행 할 수 있습니다.

        string input = "dtststx";
        char searching_char = 't';
        int output = Regex.Matches(input, "["+ searching_char +"]")[2].Index;

안부.


2

내장 IndexOf함수는 이미 문자열 내에서 문자를 검색하는 데 최적화되어 있으므로 더 빠른 버전은 다음과 같습니다 (확장 방법).

public static int NthIndexOf(this string input, char value, int n)
{
    if (n <= 0) throw new ArgumentOutOfRangeException("n", n, "n is less than zero.");

    int i = -1;
    do
    {
        i = input.IndexOf(value, i + 1);
        n--;
    }
    while (i != -1 && n > 0);

    return i;
}

또는 사용하여 문자열의 끝에서 검색합니다 LastIndexOf:

public static int NthLastIndexOf(this string input, char value, int n)
{
    if (n <= 0) throw new ArgumentOutOfRangeException("n", n, "n is less than zero.");

    int i = input.Length;
    do
    {
        i = input.LastIndexOf(value, i - 1);
        n--;
    }
    while (i != -1 && n > 0);

    return i;
}

대신 문자의 문자열을 검색하는 것은에서 매개 변수 유형을 변경하는 것만 큼 간단 charstring을 지정 과부하를 추가 선택적하고 StringComparison.


2

관심이 있다면 다음과 같이 문자열 확장 메서드를 만들 수도 있습니다.

     public static int Search(this string yourString, string yourMarker, int yourInst = 1, bool caseSensitive = true)
    {
        //returns the placement of a string in another string
        int num = 0;
        int currentInst = 0;
        //if optional argument, case sensitive is false convert string and marker to lowercase
        if (!caseSensitive) { yourString = yourString.ToLower(); yourMarker = yourMarker.ToLower(); }
        int myReturnValue = -1; //if nothing is found the returned integer is negative 1
        while ((num + yourMarker.Length) <= yourString.Length)
        {
            string testString = yourString.Substring(num, yourMarker.Length);

            if (testString == yourMarker)
            {
                currentInst++;
                if (currentInst == yourInst)
                {
                    myReturnValue = num;
                    break;
                }
            }
            num++;
        }           
       return myReturnValue;
    }

   public static int Search(this string yourString, char yourMarker, int yourInst = 1, bool caseSensitive = true)
    {
        //returns the placement of a string in another string
        int num = 0;
        int currentInst = 0;
        var charArray = yourString.ToArray<char>();
        int myReturnValue = -1;
        if (!caseSensitive)
        {
            yourString = yourString.ToLower();
            yourMarker = Char.ToLower(yourMarker);
        }
        while (num <= charArray.Length)
        {                
            if (charArray[num] == yourMarker)
            {
                currentInst++;
                if (currentInst == yourInst)
                {
                    myReturnValue = num;
                    break;
                }
            }
            num++;
        }
        return myReturnValue;
    }

2

여기에 문자열 구현이있는 더 간단한 IndexOfNth()문자열 구현이 있습니다.

다음은 string경기 버전입니다.

public static int IndexOfNth(this string source, string matchString, 
                             int charInstance, 
                             StringComparison stringComparison = StringComparison.CurrentCulture)
{
    if (string.IsNullOrEmpty(source))
        return -1;

    int lastPos = 0;
    int count = 0;

    while (count < charInstance )
    {
        var len = source.Length - lastPos;
        lastPos = source.IndexOf(matchString, lastPos,len,stringComparison);
        if (lastPos == -1)
            break;

        count++;
        if (count == charInstance)
            return lastPos;

        lastPos += matchString.Length;
    }
    return -1;
}

그리고 char매치 버전 :

public static int IndexOfNth(string source, char matchChar, int charInstance)        
{
    if (string.IsNullOrEmpty(source))
        return -1;

    if (charInstance < 1)
        return -1;

    int count = 0;
    for (int i = 0; i < source.Length; i++)
    {
        if (source[i] == matchChar)
        {
            count++;
            if (count == charInstance)                 
                return i;                 
        }
    }
    return -1;
}

이러한 저수준 구현의 경우 오버 헤드를 줄이기 위해 LINQ, RegEx 또는 재귀를 사용하지 않기를 원할 것입니다.


1

또 다른 RegEx 기반 솔루션 (예정되지 않음) :

int NthIndexOf(string s, char t, int n) {
   if(n < 0) { throw new ArgumentException(); }
   if(n==1) { return s.IndexOf(t); }
   if(t=="") { return 0; }
   string et = RegEx.Escape(t);
   string pat = "(?<="
      + Microsoft.VisualBasic.StrDup(n-1, et + @"[.\n]*") + ")"
      + et;
   Match m = RegEx.Match(s, pat);
   return m.Success ? m.Index : -1;
}

이것은 RegEx가 Matches 컬렉션을 만들도록 요구하는 것보다 약간 더 최적이어야하며 하나를 제외하고 모두 삭제합니다.


(즉, 내가 내 대답에 표시 한 것을 이후)에 일치 수집 코멘트에 대응 : 나는보다 효율적인 접근을 위해 잠시 루프 검사를 사용하는 것입니다 생각 match.Success과를 얻을 NextMatch카운터를 증가 때 초기 파괴하면서 counter == index.
Ahmad Mageed

1
    public static int FindOccuranceOf(this string str,char @char, int occurance)
    {
       var result = str.Select((x, y) => new { Letter = x, Index = y })
            .Where(letter => letter.Letter == @char).ToList();
       if (occurence > result.Count || occurance <= 0)
       {
           throw new IndexOutOfRangeException("occurance");
       }
       return result[occurance-1].Index ;
    }

1

안녕 모두 내가 n 번째 char 및 루프를 탐색하지 않고 복잡성이 적은 텍스트 를 찾기 위해 두 가지 오버로드 방법을 만들었으므로 응용 프로그램의 성능이 향상됩니다.

public static int NthIndexOf(string text, char searchChar, int nthindex)
{
   int index = -1;
   try
   {
      var takeCount = text.TakeWhile(x => (nthindex -= (x == searchChar ? 1 : 0)) > 0).Count();
      if (takeCount < text.Length) index = takeCount;
   }
   catch { }
   return index;
}
public static int NthIndexOf(string text, string searchText, int nthindex)
{
     int index = -1;
     try
     {
        Match m = Regex.Match(text, "((" + searchText + ").*?){" + nthindex + "}");
        if (m.Success) index = m.Groups[2].Captures[nthindex - 1].Index;
     }
     catch { }
     return index;
}

1

Marc Cals의 LINQ Extended for generic.

   using System;
   using System.Collections.Generic;
   using System.Linq;

   namespace fNns
   {
       public class indexer<T> where T : IEquatable<T>
       {
           public T t { get; set; }
           public int index { get; set; }
       }
       public static class fN
       {
           public static indexer<T> findNth<T>(IEnumerable<T> tc, T t,
               int occurrencePosition) where T : IEquatable<T>
           {
               var result = tc.Select((ti, i) => new indexer<T> { t = ti, index = i })
                      .Where(item => item.t.Equals(t))
                      .Skip(occurrencePosition - 1)
                      .FirstOrDefault();
               return result;
           }
           public static indexer<T> findNthReverse<T>(IEnumerable<T> tc, T t,
       int occurrencePosition) where T : IEquatable<T>
           {
               var result = tc.Reverse<T>().Select((ti, i) => new indexer<T> {t = ti, index = i })
                      .Where(item => item.t.Equals(t))
                      .Skip(occurrencePosition - 1)
                      .FirstOrDefault();
               return result;
           }
       }
   }

일부 테스트.

   using System;
   using System.Collections.Generic;
   using NUnit.Framework;
   using Newtonsoft.Json;
   namespace FindNthNamespace.Tests
   {

       public class fNTests
       {
           [TestCase("pass", "dtststx", 't', 3, Result = "{\"t\":\"t\",\"index\":5}")]
           [TestCase("pass", new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        0, 2, Result="{\"t\":0,\"index\":10}")]
           public string fNMethodTest<T>(string scenario, IEnumerable<T> tc, T t, int occurrencePosition) where T : IEquatable<T>
           {
               Console.WriteLine(scenario);
               return JsonConvert.SerializeObject(fNns.fN.findNth<T>(tc, t, occurrencePosition)).ToString();
           }

           [TestCase("pass", "dtststxx", 't', 3, Result = "{\"t\":\"t\",\"index\":6}")]
           [TestCase("pass", new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
        0, 2, Result = "{\"t\":0,\"index\":19}")]
           public string fNMethodTestReverse<T>(string scenario, IEnumerable<T> tc, T t, int occurrencePosition) where T : IEquatable<T>
           {
               Console.WriteLine(scenario);
               return JsonConvert.SerializeObject(fNns.fN.findNthReverse<T>(tc, t, occurrencePosition)).ToString();
           }


}

}


1
public static int IndexOfAny(this string str, string[] values, int startIndex, out string selectedItem)
    {
        int first = -1;
        selectedItem = null;
        foreach (string item in values)
        {
            int i = str.IndexOf(item, startIndex, StringComparison.OrdinalIgnoreCase);
            if (i >= 0)
            {
                if (first > 0)
                {
                    if (i < first)
                    {
                        first = i;
                        selectedItem = item;
                    }
                }
                else
                {
                    first = i;
                    selectedItem = item;
                }
            }
        }
        return first;
    }

-3
string theString = "The String";
int index = theString.NthIndexOf("THEVALUE", 3, true);

NthIndexOf 메서드의 정의는 어디에 있습니까?
Muhammad Waqas Aziz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.