2012-04-21 148 views
3

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

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

功能的代码工作正常,只是我穿过绳子两次感到不舒服,一次找到字符,然后找到索引。有没有可能在一次使用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()) 
+1

6000声誉一个“新手” – 2012-04-21 09:31:37

+0

@NikhilAgrawal,来自[tag:python]的6000个声誉,而不是C#:) – 2012-04-21 09:34:30

回答

6

好吧,如果你只是做到这一点在LINQ,你可以试试如果你运行这个对字符串使用类似

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

,说

"string spam = "abcdeFgihjklmnopQrstuv";" 

结果将成为:5, 16

这将返回预期结果。

+0

这是一个发电机吗?例如,如果我在上面的表达式中应用First(),它会继续评估整个字符串吗?同样对我来说,上面的生成器是相当下降的,但是你能否给出一个理由(可读性/可维护性),应该避免上述构造? – Abhijit 2012-04-21 09:46:17

+0

@Ahhijit:我用一个例子编辑了我的帖子。关于构造,关于可读性,我完全知道的往往是纯粹的主观的,所以对于**我来说,简单的'foreach'看起来更可读,LINQ,在这种情况下*。这是一个选择的问题。选择你喜欢的*。 – Tigran 2012-04-21 09:50:00

+0

由于上面的返回IEnumerable,我想知道的是,如果上述表达式是[generator](http://en.wikipedia。org/wiki/Generator_%28computer_programming%29#Generator_Expressions)或[comprehension](http://en.wikipedia.org/wiki/List_comprehension)。在我的问题中,我有一个Python例子,它应该阐明我的意图。 – Abhijit 2012-04-21 09:56:40

1

无需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评论的扩展方法。

+0

或者简单的扩展方法。 LINQ很好,但谨慎使用它不仅仅是*无处不在*。 – Tigran 2012-04-21 09:25:58

+0

@Oded:感谢您的回复。我知道这可以通过简单的迭代轻松完成。我试图弄清楚LINQ是否有适合的功能。当然,如果不存在的话,经典的“foreach”会做得很好。 – Abhijit 2012-04-21 09:27:35

+1

@Abhijit - 这是_possible_在LINQ,但比值得更多的麻烦(查询需要保持指数的跟踪等) – Oded 2012-04-21 09:28:43

2

这是基于@狄格兰的一个答案,但将返回全部大写的指数,即使是重复的:

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

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

选择具有一种获取索引以及列举项目的方法。 尽管它已经给出了一个轻微的额外开销,你可以这样做:

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和index在一起。

这只是正常的Linq语法,而不是Linq查询语法。

0

另一种可能性:

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大写字符。如果您只想要英语,请将表达式更改为"[A-Z]"

0

的LINQ不提供这样的开箱的有效方式,但你可以扩展LINQ到很容易提供这种能力。

如果这个类添加到您的解决方案:

internal static class EnumerableExtensions 
{ 
    public static int IndexOfNth<T>(this IEnumerable<T> enumerable, Func<T, bool> 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<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate) 
    { 
     return enumerable.IndexOfNth(predicate); 
    } 
} 

然后,你可以写这样的代码,这将是有效的,只列举串一次:

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

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

使用这种实现,你也可以找到第二个大写字母是这样的:

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

而且它与字符的任何枚举不只是枚举的作品,例如

var indexOfFirstManager = employees.IndexOfFirst(employee => employee.IsManager); 
相关问题