【问题标题】:Find Index of the First Uppercase character查找第一个大写字符的索引
【发布时间】:2012-05-02 17:12:14
【问题描述】:

作为一个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())

【问题讨论】:

  • 6000 声望的“新手”
  • @NikhilAgrawal,来自python 的 6000 个声誉,而不是 C# :)

标签: c# string linq search


【解决方案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 cmets。

【讨论】:

  • 或者只是一种扩展方法。 LINQ 很好,但请谨慎使用,而不仅仅是无处不在
  • @Oded:感谢您的回复。我知道这可以通过简单的迭代轻松完成。我试图弄清楚 LINQ 是否存在合适的功能。当然,如果不存在,经典的foreach 会做得很好。
  • @Abhijit - 在 LINQ 中可能,但麻烦多于值得(查询需要跟踪索引等...)
  • @Oded: :-),请您不厌其烦地至少启发灰质,这样像我这样的新手就可以陷入陷阱并避免这种结构。
  • @Abhijit - 仅此而已,我想不出在 LINQ 中执行此操作的好方法...而且我宁愿不发布 bad 代码。
【解决方案2】:

好吧,如果您只是想要LINQ 中执行此操作,您可以尝试使用类似

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

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

"string spam = "abcdeFgihjklmnopQrstuv";"

结果将是:5, 16

这将返回预期的结果。

【讨论】:

  • 这会是发电机吗?例如,如果我在上面的表达式上应用First(),它会继续评估整个字符串吗?对我来说,上面的生成器也很下降,但你能否给出一个应该避免上述构造的原因(可读性/可维护性)?
  • @Abhijit:我用一个例子编辑了我的帖子。构造怎么样,它只是关于可读性,我完全意识到这通常是纯粹主观的,所以 me 的简单 foreach 似乎更具可读性,LINQ,在 this案例。这是一个选择问题。选择喜欢什么。
  • 由于上面返回一个IEnumerable,我想知道上面的表达式是generator还是comprehension。在我的问题中,我有一个 Python 示例,它应该阐明我的意图。
  • @Abhijit: no in this case you can't control iteration behavior in the loop(因为 generator 代表什么)。在这里,您查询数据并对其进行迭代。如果你想这样做,你应该使用foreachyield 组合。
  • 您可以保存ToArray() 调用,因为stringIEnumerable<char>
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

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

    如果您需要同时使用 char 和索引,则可以遍历对象。

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

    【讨论】:

      【解决方案5】:

      另一种可能性:

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:
          String.Concat(
            str.Select(c => Char.IsUpper(c) ? $" {c}" : $"{c}")
          );
          

          【讨论】:

            猜你喜欢
            • 2011-05-29
            • 1970-01-01
            • 2017-02-23
            • 2020-08-02
            • 2021-01-13
            • 2017-09-05
            • 1970-01-01
            • 2012-11-24
            • 2012-04-25
            相关资源
            最近更新 更多