获取C#字符串中第一个非空白字符的索引

时间:2012-10-02 17:47:48

标签: c#

有没有办法在C#中获取字符串中第一个非空白字符的索引(或者更一般地说,是第一个匹配条件的索引)而不编写我自己的循环代码?

修改

通过“编写我自己的循环代码”,我的意思是我正在寻找一个能够解决问题的紧凑表达式,而不会混淆我正在处理的逻辑。

对于那一点上的任何混淆我道歉。

11 个答案:

答案 0 :(得分:34)

string当然是IEnumerable<char>,因此您可以使用Linq:

int offset = someString.TakeWhile(c => char.IsWhiteSpace(c)).Count();

答案 1 :(得分:12)

我喜欢定义自己的扩展方法,用于返回满足序列中自定义谓词的第一个元素的索引。

/// <summary>
/// Returns the index of the first element in the sequence 
/// that satisfies a condition.
/// </summary>
/// <typeparam name="TSource">
/// The type of the elements of <paramref name="source"/>.
/// </typeparam>
/// <param name="source">
/// An <see cref="IEnumerable{T}"/> that contains
/// the elements to apply the predicate to.
/// </param>
/// <param name="predicate">
/// A function to test each element for a condition.
/// </param>
/// <returns>
/// The zero-based index position of the first element of <paramref name="source"/>
/// for which <paramref name="predicate"/> returns <see langword="true"/>;
/// or -1 if <paramref name="source"/> is empty
/// or no element satisfies the condition.
/// </returns>
public static int IndexOf<TSource>(this IEnumerable<TSource> source, 
    Func<TSource, bool> predicate)
{
    int i = 0;

    foreach (TSource element in source)
    {
        if (predicate(element))
            return i;

        i++;
    }

    return -1;
}

然后,您可以使用LINQ解决原始问题:

string str = "   Hello World";
int i = str.IndexOf<char>(c => !char.IsWhiteSpace(c));

答案 2 :(得分:4)

string s= "   \t  Test";
Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x));

返回6

添加条件只需...

Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x) && your condition);

答案 3 :(得分:3)

您可以使用String.IndexOfAny函数返回指定Unicode字符数组中任何字符的第一个匹配项。

或者,您可以使用String.TrimStart函数删除字符串开头的所有空格字符。第一个非空格字符的索引是原始字符串的长度与修剪过的字符串之间的差异。

你甚至可以选择一组要修剪的角色:)

基本上,如果你正在寻找一组有限的字符(让我们说数字),你应该选择第一种方法。

如果你试图忽略一组有限的字符(比如空格),你应该选择第二种方法。

最后一种方法是使用Linq方法:

string s = "        qsdmlkqmlsdkm";
Console.WriteLine(s.TrimStart());
Console.WriteLine(s.Length - s.TrimStart().Length);
Console.WriteLine(s.FirstOrDefault(c => !Char.IsWhiteSpace(c)));
Console.WriteLine(s.IndexOf(s.FirstOrDefault(c => !Char.IsWhiteSpace(c))));

输出:

qsdmlkqmlsdkm
8
q
8

答案 4 :(得分:3)

var match = Regex.Match(" \t test  ", @"\S"); // \S means all characters that are not whitespace
if (match.Success)
{
    int index = match.Index;
    //do something with index
}
else
{
    //there were no non-whitespace characters, handle appropriately
}

如果您经常这样做,出于性能原因,您应该为此模式缓存已编译的Regex,例如:

static readonly Regex nonWhitespace = new Regex(@"\S");

然后使用它:

nonWhitespace.Match(" \t test  ");

答案 5 :(得分:3)

由于这里有几个解决方案,我决定做一些性能测试,看看每个解决方案的表现。决定为那些感兴趣的人分享这些结果......

    int iterations = 1000000;
    int result = 0;
    string s= "   \t  Test";

    System.Diagnostics.Stopwatch watch = new Stopwatch();

    // Convert to char array and use FindIndex
    watch.Start();
    for (int i = 0; i < iterations; i++)
        result = Array.FindIndex(s.ToCharArray(), x => !char.IsWhiteSpace(x)); 
    watch.Stop();
    Console.WriteLine("Convert to char array and use FindIndex: " + watch.ElapsedMilliseconds);

    // Trim spaces and get index of first character
    watch.Restart();
    for (int i = 0; i < iterations; i++)
        result = s.IndexOf(s.TrimStart().Substring(0,1));
    watch.Stop();
    Console.WriteLine("Trim spaces and get index of first character: " + watch.ElapsedMilliseconds);

    // Use extension method
    watch.Restart();
    for (int i = 0; i < iterations; i++)
        result = s.IndexOf<char>(c => !char.IsWhiteSpace(c));
    watch.Stop();
    Console.WriteLine("Use extension method: " + watch.ElapsedMilliseconds);

    // Loop
    watch.Restart();
    for (int i = 0; i < iterations; i++)
    {   
        result = 0;
        foreach (char c in s)
        {
            if (!char.IsWhiteSpace(c))
                break;
            result++;
        }
    }
    watch.Stop();
    Console.WriteLine("Loop: " + watch.ElapsedMilliseconds);

结果以毫秒为单位....

其中s =“\ t测试”
转换为char数组并使用FindIndex:154
修剪空格并获取第一个字符的索引:189
使用扩展方法:234
循环:146

其中s =“测试”
转换为char数组并使用FindIndex:39
修剪空格并获取第一个字符的索引:155
使用扩展方法:57
循环:15

其中s =(1000个不带空格的字符串)
转换为char数组并使用FindIndex:506
修剪空格并获取第一个字符的索引:534
使用扩展方法:51
循环:15

其中s =(1000个字符串以“\ t Test”开头)
转换为char数组并使用FindIndex:609
修剪空格并获取第一个字符的索引:1103
使用扩展方法:226
循环:146

  

得出你自己的结论,但我的结论是使用任何一个   你最喜欢,因为性能差异微不足道   现实世界的场景。

答案 6 :(得分:1)

您可以修剪,获取第一个字符并使用IndexOf。

答案 7 :(得分:1)

有一个非常简单的解决方案

string test = "    hello world";
int pos = test.ToList<char>().FindIndex(x => char.IsWhiteSpace(x) == false);

pos将是4

你可以有更复杂的条件,如:

pos = test.ToList<char>().FindIndex((x) =>
                {
                    if (x == 's') //Your complex conditions go here
                        return true;
                    else 
                        return false;
                }
            );

答案 8 :(得分:0)

是的,你可以试试这个:

string stg = "   xyz";
int indx = (stg.Length - stg.Trim().Length);  

答案 9 :(得分:0)

某些东西会在某个地方循环。要完全控制什么是空白,你可以使用linq来对象进行循环:

int index = Array.FindIndex(
               s.ToCharArray(), 
               x => !(new [] { '\t', '\r', '\n', ' '}.Any(c => c == x)));

答案 10 :(得分:0)

这里有很多解决方案将字符串转换为数组。这不是必需的,字符串中的单个字符可以像数组中的项一样被访问。

这是我应该非常有效的解决方案:

private static int FirstNonMatch(string s, Func<char, bool> predicate, int startPosition = 0)
{
    for (var i = startPosition; i < s.Length; i++)
        if (!predicate(s[i])) return i;

    return -1;
}

private static int LastNonMatch(string s, Func<char, bool> predicate, int startPosition)
{
    for (var i = startPosition; i >= 0; i--)
        if (!predicate(s[i])) return i;

    return -1;
}

要使用它们,请执行以下操作:

var x = FirstNonMatch(" asdf ", char.IsWhiteSpace);
var y = LastNonMatch(" asdf ", char.IsWhiteSpace, " asdf ".Length);