查找第一个大写字符的索引

作为一个C#新手,目前要找出一个字符串中第一个大写字符的索引,我已经想出了办法

var pos = spam.IndexOf(spam.ToCharArray().First(s => String.Equals(s, char.ToUpper(s)))); 

从function上来说,代码工作正常,只是我有两次遍历字符串的不适,一次找到字符然后找到索引。 是否有可能使用LINQ在一次传递中获取第一个UpperCase字符的索引?

C ++中的等价方式就像是

 std::string::const_iterator itL=find_if(spam.begin(), spam.end(),isupper); 

一个等效的Python语法

 next(i for i,e in enumerate(spam) if e.isupper()) 

好吧,如果你只是LINQ做,你可以尝试使用类似的东西

 (from ch in spam.ToArray() where Char.IsUpper(ch) select spam.IndexOf(ch)) 

如果你对字符串运行,比如说

 "string spam = "abcdeFgihjklmnopQrstuv";" 

结果将是: 5, 16

这将返回预期的结果。

这是基于@ Tigran的答案,但即使有重复,也会返回所有大写的索引:

 from i in Enumerable.Range(0, searchText.Length - 1) where Char.IsUpper(searchText.Chars[i]) select i 

仅仅因为我经常发现人们不知道:

选择有一种方法来获取索引以及您要枚举的项目。 虽然它会产生一些额外的开销,但你可以这样做:

 var objectsWithCharAndIndex = myString.Select((c,i)=>new {Char = c,Index = i}); 

那么它唯一的问题是:

 var firstCapitalIndex = objectsWithCharAndIndex.First(o => Char.IsUpper(o.Char)).Index; 

或者全部归还

 var allCapitalIndexes = objectsWithCharAndIndex.Where(o => Char.IsUpper(o.Char)).Select(o=>o.Index); 

如果你需要char和索引,你可以迭代对象。

这只是普通的Linq语法,而不是Linq查询语法。

不需要LINQ:

 int index = 0; foreach(Char c in myString) { if(Char.IsUpper(c)) break; index++; } // index now contains the index of the first upper case character 

这可以很容易地转换为扩展方法,如@Tigran评论。

另一种可能性

 string s = "0123aBc"; int rslt = -1; var ch = s.FirstOrDefault(c => char.IsUpper(c)); if (ch != 0) rslt = s.IndexOf(ch); 

而且,如果你只关心英语:

 char[] UpperCaseChars = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; int rslt = s.IndexOfAny(UpperCaseChars); 

这可能会比其他任何一个执行得快得多,而且如果我这么做的话,我就会使用它。

另一种可能性是使用正则表达式:

 var match = Regex.Match(s, "\p{Lu}", RegexOptions.None); int rslt = match.Success ? match.Index : -1; 

这将匹配任何Unicode大写字符。 如果您只想要英语,请将表达式更改为"[AZ]"

Linq没有提供开箱即用的有效方法,但您可以非常轻松地扩展Linq以提供此function。

如果将此类添加到解决方案中:

 internal static class EnumerableExtensions { public static int IndexOfNth(this IEnumerable enumerable, Func predicate, int skip = 0) { var index = 0; foreach (var value in enumerable) { if (predicate(value)) { if (skip-- == 0) return index; } index++; } return -1; } public static int IndexOfFirst(this IEnumerable enumerable, Func predicate) { return enumerable.IndexOfNth(predicate); } } 

然后你可以编写这样有效的代码并且只枚举一次字符串:

 var firstUpper = spam[spam.IndexOfFirst(Char.IsUpper)]; 

注意:如果字符串不包含任何大写字母,这会导致索引超出范围,因此您需要额外检查IndexOfFirst返回-1

通过此实现,您还可以找到第二个大写字母,如下所示:

 var secondUpper = spam[spam.IndexOfNth(Char.IsUpper, 1)]; 

例如,它适用于任何可枚举的chars枚举

 var indexOfFirstManager = employees.IndexOfFirst(employee => employee.IsManager);