繁体   English   中英

正则表达式匹配连续数字

[英]Regular expression to match consecutive numbers

我正在尝试从以下具有连续数字的字符串中提取部分:

word 7, word 8, word 9, word 14

所以我得到:

word 7, word 8, word 9
word 14

使用正则表达式。 我所做的是使用(word (?<num>\\d+),?\\s*)+ ,然后检查每次捕获的数字。

是否可以使用正则表达式直接提取仅包含连续数字的部分?

由于非RegEx解决方案是可以接受的:

var data = "word 7, word 8, word 9, word 14";

// split the data into word and number
var dataCollection = data.Split(',').Select (d => new 
{ 
    word = d.Trim().Split(' ')[0], 
    number = int.Parse(d.Trim().Split(' ')[1]) 
}).ToList();

// store each set of consective results into a collection
List<string> resultsCollection = new List<string>();
var sb = new StringBuilder();
int i = 0;
while(i < dataCollection.Count ())
{
    if(i > 0)
    {
       if(dataCollection[i].number == dataCollection[i-1].number + 1)
       {
           if(sb.Length > 0) sb.Append(", ");
       }
       else
       {
          resultsCollection.Add(sb.ToString());
          sb.Clear();
       }
    }
    sb.AppendFormat("{0} {1}", dataCollection[i].word, dataCollection[i].number);
    i++;
}
resultsCollection.Add(sb.ToString());

对于您的测试数据, resultsCollection将包含两项:

单词7,单词8,单词9

字14

仅使用正则表达式是不可能的,因为正则表达式只能描述正则语言

除其他限制外,常规语言不允许定义上下文,在您的情况下,这将是字符串中最新的遇到的数字。

有关语言和语法理论的更多信息,请参见Chomsky层次结构

或者,您可以使用:

        string words = "word 7, word 8, word 9, word 14";
        string[] splittedWords = Regex.Split(words, ", "); //Separating words.

        List<string> sortedWords = new List<string>();

        int currentWordNumber = 0, lastWordNumber = 0;
        foreach (string sptw in splittedWords)
        {
            if (sortedWords.Count == 0) //No value has been written to the list yet, so:
            {
                sortedWords.Add(sptw);
                lastWordNumber = int.Parse(sptw.Split(' ')[1]); //Storing the number of the word for checking it later.
            }
            else
            {
                currentWordNumber = int.Parse(sptw.Split(' ')[1]);

                if (currentWordNumber == lastWordNumber + 1)
                    sortedWords[sortedWords.Count - 1] += ", " + sptw;
                else
                    sortedWords.Add(sptw);

                lastWordNumber = currentWordNumber; //Storing the number of the word for checking it later.
            }
        }

最后, sortedWords列表将具有:

"word 7, word 8, word 9"
"word 14"

LINQ非常适合各种序列。 它有许多有用的运算符,但您也可以定义自己的运算符。 使用方法如下:

   "word 10, word 11, word 7, word 8, word 9, word 14, word 2"
        .Split( new [] {", "}, StringSplitOptions.RemoveEmptyEntries)
        .ToPartitionsOfConsecutiveValues(w => Int32.Parse(w.Split(' ').Last()))
        .Select(sequence => String.Join(", ", sequence))
        .ToArray()
        .Dump("Array of strings");

Dump来自LINQPad。

这是新的运算符:

public static class Partition {

    public static IEnumerable<List<T>> ToPartitionsOfConsecutiveValues<T>(
        this IEnumerable<T> source, 
        Func<T,int> valueSelector)
    {
        var lastValue = (int?)null;
        List<T> lastList = null;    
        foreach (var item in source) 
        {
            var value = valueSelector(item);
            if (!(lastValue.HasValue)) 
            {
                lastList = new List<T>();
            }
            else if (lastValue.Value != value - 1)            
            {
                yield return lastList;
                lastList = new List<T>();
            }
            lastValue = value;
            lastList.Add(item);
        }
        if (lastValue.HasValue) yield return lastList;
    }
}

根据@LB的评论进行更新

如果LINQ运算符的具体类型越少越有用。 拔出使用项目类型( int )的谓词可以在其他情况下使用运算符。

这是相同的示例:

Func<String,Int32> IntSuffix = w => Int32.Parse(w.Split(' ').Last());
Func<String, String, Boolean> breakPredicate 
    = (prev, next) => IntSuffix(prev) != IntSuffix(next) - 1;
s.Split( new [] {", "}, StringSplitOptions.RemoveEmptyEntries)
    .ToPartitionsOfSequences(breakPredicate)
    .Select (sequence => String.Join(", ", sequence))

实现:

public static IEnumerable<List<T>> ToPartitionsOfSequences<T>(
    this IEnumerable<T> source, 
    Func<T, T, Boolean> breakPredicate)
{
    T lastItem = default(T);
    List<T> lastList = null;    
    foreach (var item in source) 
    {
        if (lastList == null) 
        {
            lastList = new List<T>();
        }
        else if (breakPredicate(lastItem, item))
        {
            yield return lastList;
            lastList = new List<T>();
        }
        lastItem = item;
        lastList.Add(item);
    }
    if (lastList != null) yield return lastList;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM