Я отредактировал ваш ответ, чтобы более четко передать то, что вы хотите. Надеюсь, вы получите ответы на свой вопрос. Отсутствие свободного владения английским не является проблемой для Stack Overflow, вы всегда можете просто добавить строку с просьбой к кому-то более беглому отредактировать ваш вопрос и очистить его, но вы сами должны стремиться предоставить несколько примеров в вопросе, чтобы люди понимали, что тебе нужно.
Лассе В. Карлсен
Ответы:
94
publicintGetNthIndex(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;
}
Это можно было бы сделать намного чище, и на вводе нет проверок.
Отличный подход. Красивый и чистый, легко читаемый, простой в обслуживании и отличной производительности.
Майк
1
Любите такие лупы, они не только дают отличную производительность, но и вы не ошибетесь с ними, потому что все кристально чистое и прямо у вас на глазах. Вы пишете linq, и какой-то разработчик зацикливает его, не понимая стоимости, и все задаются вопросом, где же узкое место в производительности.
Он возвращает длину / количество строки s. Вам нужно проверить это значение.
Yoky
10
Вот еще одно решение LINQ:
string input = "dtststx";
char searchChar = 't';
int occurrencePosition = 3; // third occurrence of the charvar 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!");
Вот рекурсивная реализация - как метод расширения, имитирующий формат метода (ов) фреймворка:
publicstaticintIndexOfNth(thisstring input, stringvalue, int startIndex, int nth)
{
if (nth < 1)
thrownew 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), которые могут вам помочь (чтобы доказать, что это правильно):
Ваш однострочный пример не работает, потому что значение n никогда не изменяется.
Dave Neeley
2
Хорошее решение, хотя это не настоящий «однострочник», поскольку переменная должна быть определена вне области лямбда. s.TakeWhile (c => ((n - = (c == 't'))? 1: 0)> 0) .Count ();
обнуляется
12
−1, "так что я оставил там несколько ошибок, чтобы вы могли их найти"
Занон
6
Раномор правильно заметил, что однострочный вариант Джоэла Кохорна не работает.
Вот двухстрочный, который действительно работает, метод расширения строки, который возвращает отсчитываемый от 0 индекс n-го вхождения символа или -1, если n-го вхождения не существует:
string result = "i am 'bansal.vks@gmail.com'"; // stringint in1 = result.IndexOf('\''); // get the index of first quoteint in2 = result.IndexOf('\'', in1 + 1); // get the index of secondstring quoted_text = result.Substring(in1 + 1, in2 - in1); // get the string between quotes
Поскольку встроенная IndexOfфункция уже оптимизирована для поиска символа в строке, более быстрая версия будет (как метод расширения):
publicstaticintNthIndexOf(thisstring input, charvalue, int n)
{
if (n <= 0) thrownew 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:
publicstaticintNthLastIndexOf(thisstring input, charvalue, int n)
{
if (n <= 0) thrownew 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;
}
Искать строку вместо символа так же просто, как изменить тип параметра с charна stringи, при необходимости, добавить перегрузку, чтобы указать StringComparison.
Вот еще одна, может быть, более простая реализация строки IndexOfNth() с реализацией строк.
Вот stringверсия матча:
publicstaticintIndexOfNth(thisstring 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версия матча:
publicstaticintIndexOfNth(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 или рекурсии для уменьшения накладных расходов.
В ответ на комментарий коллекции Matches (поскольку это то, что я показал в своем ответе): я полагаю, более эффективным подходом было бы использование проверки цикла while match.Successи получение NextMatchwhile, увеличивая счетчик и преждевременно прерываясь, когда counter == index.
Ахмад Магид
1
publicstaticintFindOccuranceOf(thisstring 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)
{
thrownew IndexOutOfRangeException("occurance");
}
return result[occurance-1].Index ;
}
Привет всем, я создал два метода перегрузки для поиска n-го вхождения символа и для текста с меньшей сложностью без навигации по циклу, что увеличивает производительность вашего приложения.
publicstaticintNthIndexOf(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;
}
publicstaticintNthIndexOf(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;
}
using System;
using System.Collections.Generic;
using System.Linq;
namespacefNns
{
publicclassindexer<T> whereT : IEquatable<T>
{
public T t { get; set; }
publicint index { get; set; }
}
publicstaticclassfN
{
publicstaticindexer<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;
}
publicstaticindexer<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;
namespaceFindNthNamespace.Tests
{
publicclassfNTests
{
[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}")]
publicstringfNMethodTest<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}")]
publicstringfNMethodTestReverse<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();
}
}
publicstaticintIndexOfAny(thisstring str, string[] values, int startIndex, outstring 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;
}
Ответы:
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; }
Это можно было бы сделать намного чище, и на вводе нет проверок.
источник
В предыдущем решении есть небольшая ошибка.
Вот обновленный код:
s.TakeWhile(c => (n -= (c == t ? 1 : 0)) > 0).Count();
источник
Вот еще одно решение 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!");
источник
Вот рекурсивная реализация - как метод расширения, имитирующий формат метода (ов) фреймворка:
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)); }
источник
Обновление: Индекс 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; }
источник
Раномор правильно заметил, что однострочный вариант Джоэла Кохорна не работает.
Вот двухстрочный, который действительно работает, метод расширения строки, который возвращает отсчитываемый от 0 индекс n-го вхождения символа или -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; } }
источник
Ответ Джоэла хороший (и я его поддержал). Вот решение на основе LINQ:
yourString.Where(c => c == 't').Count();
источник
Where
и передав предикатCount
методу. Не то, чтобы что-то не так.Я добавляю еще один ответ, который работает довольно быстро по сравнению с другими методами
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; }
источник
Вот забавный способ сделать это
int i = 0; string s="asdasdasd"; int n = 3; s.Where(b => (b == 'd') && (i++ == n)); return i;
источник
public int GetNthOccurrenceOfChar(string s, char c, int occ) { return String.Join(c.ToString(), s.Split(new char[] { c }, StringSplitOptions.None).Take(occ)).Length; }
источник
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
источник
вы можете сделать это с помощью регулярных выражений.
string input = "dtststx"; char searching_char = 't'; int output = Regex.Matches(input, "["+ searching_char +"]")[2].Index;
с наилучшими пожеланиями.
источник
Поскольку встроенная
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; }
Искать строку вместо символа так же просто, как изменить тип параметра с
char
наstring
и, при необходимости, добавить перегрузку, чтобы указатьStringComparison
.источник
если вам интересно, вы также можете создать такие методы расширения строк:
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; }
источник
Вот еще одна, может быть, более простая реализация строки
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 или рекурсии для уменьшения накладных расходов.
источник
Другое решение на основе 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
while, увеличивая счетчик и преждевременно прерываясь, когдаcounter == index
.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 ; }
источник
Привет всем, я создал два метода перегрузки для поиска 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; }
источник
Расширенный LINQ Марка Калса для универсальных.
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(); } }
}
источник
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; }
источник
string theString = "The String"; int index = theString.NthIndexOf("THEVALUE", 3, true);
источник