4

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

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

从功能上讲,代码工作正常,只是我对遍历字符串两次感到不舒服,一次是找到字符,然后是索引。是否有可能使用 LINQ 一次性获取第一个大写字符的索引?

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())
4

7 回答 7

7

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

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

如果你对字符串运行这个,说

"string spam = "abcdeFgihjklmnopQrstuv";"

结果将是:5, 16

这将返回预期的结果。

于 2012-04-21T09:36:08.137 回答
3

另一种可能:

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]"

于 2013-04-19T12:05:59.630 回答
2

不需要 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 评论的那样,这可以很容易地转换为扩展方法。

于 2012-04-21T09:24:16.367 回答
2

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

from i in Enumerable.Range(0, searchText.Length - 1) 
      where Char.IsUpper(searchText.Chars[i]) select i
于 2013-04-19T11:07:21.077 回答
2

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

Select 有一种方法可以获取索引以及您正在枚举的项目。尽管它会带来一点额外的开销,但您可以这样做:

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

如果您需要将字符和索引放在一起,您可以遍历对象。

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

于 2013-04-19T11:39:37.037 回答
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);
于 2017-09-07T23:14:43.993 回答
0
String.Concat(
  str.Select(c => Char.IsUpper(c) ? $" {c}" : $"{c}")
);
于 2019-07-10T05:34:36.333 回答