2012-04-21 124 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); 
相關問題