Tìm lần xuất hiện thứ N của một ký tự trong một chuỗi


83

Tôi cần trợ giúp về việc tạo phương thức C # trả về chỉ số của lần xuất hiện thứ N của một ký tự trong một chuỗi.

Ví dụ: lần xuất hiện thứ 3 của ký tự 't'trong chuỗi "dtststxtu"là 5
(Lưu ý rằng chuỗi có 4 ts.)


Bạn phải làm việc với những gì cho đến nay?
Anthony Forloney

3
Tôi đã chỉnh sửa câu trả lời của bạn để truyền đạt rõ ràng hơn những gì bạn muốn. Hy vọng rằng bạn sẽ nhận được một số câu trả lời phù hợp với câu hỏi. Không thông thạo tiếng Anh không phải là vấn đề trên Stack Overflow, bạn luôn có thể thêm một dòng yêu cầu ai đó thông thạo hơn chỉnh sửa câu hỏi của bạn và làm sạch câu hỏi của bạn, nhưng bản thân bạn phải cố gắng cung cấp một số ví dụ trong câu hỏi để mọi người hiểu điều gì bạn cần.
Lasse V. Karlsen

Câu trả lời:


93
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;
}

Điều đó có thể được làm sạch hơn rất nhiều và không có kiểm tra đầu vào.


7
Cách tiếp cận tuyệt vời. Đẹp và sạch sẽ, dễ đọc, dễ bảo trì và hiệu suất tuyệt vời.
Mike

1
tình yêu đối với các vòng lặp như thế này, không chỉ chúng cho hiệu suất tuyệt vời mà bạn không thể làm sai với chúng vì mọi thứ đều rõ ràng và ngay trước mắt bạn. Bạn viết một linq và một số nhà phát triển đặt nó vào một vòng lặp không hiểu chi phí và mọi người cứ tự hỏi đâu là điểm nghẽn hiệu suất.
user734028

20

Có một lỗi nhỏ trong giải pháp trước đó.

Đây là một số mã được cập nhật:

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

1
Nó trả về cái gì nếu ký tự không được tìm thấy?
Timuçin

Nó trả về độ dài / số lượng của chuỗi s. Bạn cần kiểm tra giá trị đó.
Yoky

10

Đây là một giải pháp LINQ khác:

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);
}

Chỉ cho vui thôi, đây là một giải pháp của Regex. Tôi thấy một số người ban đầu sử dụng Regex để đếm, nhưng khi câu hỏi thay đổi, không có cập nhật nào được thực hiện. Đây là cách nó có thể được thực hiện với Regex - một lần nữa, chỉ cho vui. Cách tiếp cận truyền thống là tốt nhất cho sự đơn giản.

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

Đây là cách triển khai đệ quy - như một phương thức mở rộng, bắt chước định dạng của (các) phương thức khung:

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);
}

Ngoài ra, đây là một số bài kiểm tra đơn vị (MBUnit) có thể giúp bạn (để chứng minh nó là đúng):

[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

Cập nhật: Chỉ số của lớp lót một lớp bảo hiểm thứ N:

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

Sử dụng những rủi ro của riêng bạn. Đây trông giống như bài tập về nhà, vì vậy tôi đã để lại một vài lỗi trong đó để bạn tìm:

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
Ví dụ một lớp lót của bạn không hoạt động vì giá trị của n không bao giờ thay đổi.
Dave Neeley

2
Giải pháp tốt, mặc dù đây không phải là "một lớp lót" thực sự vì một biến cần được xác định ngoài phạm vi của lambda. s.TakeWhile (c => ((n - = (c == 't'))? 1: 0)> 0) .Count ();
nullable

12
-1, "vì vậy tôi đã để lại một vài lỗi trong đó cho bạn để tìm"
Zanon

6

ranomore đã nhận xét chính xác rằng một lớp lót của Joel Coehoorn không hoạt động.

Đây là một hai-liner mà không làm việc, một phương pháp mở rộng chuỗi trả về chỉ số 0 dựa trên sự xuất hiện thứ n của một nhân vật, hoặc -1 nếu không xuất hiện thứ n tồn tại:

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

Câu trả lời của Joel là tốt (và tôi đã ủng hộ nó). Đây là một giải pháp dựa trên LINQ:

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

2
@Andrew - bạn có thể rút ngắn điều này bằng cách bỏ qua Wherevà chuyển vị từ cho Countphương thức. Không phải là có bất cứ điều gì sai với cách nó được.
Mike Hai

10
Điều này sẽ không chỉ tìm thấy có bao nhiêu lần xuất hiện của một ký tự thay vì chỉ số của ký tự thứ n?
dx_over_dt

4

Tôi thêm một câu trả lời khác chạy khá nhanh so với các phương pháp khác

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

Đây là một cách thú vị để làm điều đó

     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

bạn có thể thực hiện công việc này với Biểu thức chính quy.

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

trân trọng.


2

IndexOfhàm tích hợp sẵn đã được tối ưu hóa để tìm kiếm một ký tự trong một chuỗi, một phiên bản thậm chí còn nhanh hơn sẽ là (dưới dạng phương thức mở rộng):

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;
}

Hoặc để tìm kiếm từ cuối chuỗi bằng cách sử dụng 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;
}

Tìm kiếm một chuỗi thay vì một ký tự đơn giản như thay đổi loại tham số từ charthành stringvà tùy chọn thêm một quá tải để chỉ định StringComparison.


2

nếu bạn quan tâm, bạn cũng có thể tạo các phương thức mở rộng chuỗi như vậy:

     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

Đây là một cách triển khai chuỗi khác, có thể đơn giản hơn IndexOfNth()với việc triển khai chuỗi.

Đây là stringphiên bản phù hợp:

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;
}

charphiên bản đối sánh:

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;
}

Tôi nghĩ rằng đối với việc triển khai mức thấp như vậy, bạn muốn tránh sử dụng LINQ, RegEx hoặc đệ quy để giảm chi phí.


1

Một giải pháp dựa trên RegEx khác (chưa được kiểm tra):

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;
}

Điều này sẽ tối ưu hơn một chút so với việc yêu cầu RegEx tạo bộ sưu tập Matches, chỉ để loại bỏ tất cả trừ một kết quả phù hợp.


Để trả lời nhận xét về bộ sưu tập Matches (vì đó là những gì tôi đã hiển thị trong phản hồi của mình): Tôi cho rằng một cách tiếp cận hiệu quả hơn sẽ là sử dụng vòng lặp while kiểm tra match.Successvà nhận NextMatchtrong khi tăng bộ đếm và ngắt sớm khi 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

Hi tất cả tôi đã tạo ra hai phương pháp quá tải cho việc tìm kiếm xuất hiện thứ n của char và cho văn bản với ít phức tạp mà không cần điều hướng thông qua vòng lặp, trong đó tăng hiệu suất của ứng dụng của bạn.

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 Mở rộng cho chung.

   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;
           }
       }
   }

Một số thử nghiệm.

   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);

định nghĩa phương pháp NthIndexOf của bạn ở đâu?
Muhammad Waqas Aziz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.