正则表达式 求救:匹配连续数字如,123,456.

hweing 2009-08-12 04:38:14
各位好,
小弟近日 有一需求,需要匹配连续的数字,如123,456,正则表达式可以解决这个问题吗?
...全文
5248 39 打赏 收藏 转发到动态 举报
写回复
用AI写文章
39 条回复
切换为时间正序
请发表友善的回复…
发表回复
windinwing 2009-08-14
  • 打赏
  • 举报
回复
string str ="987654321";
Regex r = new Regex(@"\d+");
Match m= r.Match(str);
if(m.Success)
{
bool success=true;
string value = m.Value;
char current = value[0];
for (int i =1; i < value.Length; i++)
{
if (current <= value[i])
{
success = false;
break;
}
current = value[i];
}
if(success)
MessageBox.Show(value);
}

先匹配数字, 然后计算前一个是不是比前前一个小.这个是逆序,改为 if (current>= value[i])是升序了

hweing 2009-08-14
  • 打赏
  • 举报
回复
更正,DFA的测试结果为。耗时:11796982(Ticks) 823(毫秒).由于测试代码中包含了StringBuilder保存测试结果,导致DFA的测试失败。

最后优化的自定义C#算法:
测试结果:
耗时:8449540(Ticks) 590(毫秒)
结果测试发现,正则表达式、DFA方法、自定义算法,中,自定义算法性能是最好的,且内存占用量是最小的。
现准备结贴,感谢大家的支持。 再次谢谢大家。

/// <summary>
/// 匹配手机号码连续出现的号码片段。
/// 号码连续降序或者连续升序长度大于3的号码片段,如果有多个匹配,取字符串最长的一组。
/// 例如:13712343234,则取1234号码片段。
/// </summary>
/// <param name="input"></param>
private String Match5(string input)
{
//最后一次匹配的起始位置
Int32 startIndex = 0;
Int32 lastSeriesLength = 0;
Int32 lastStartIndex = 0;
//上一次序列增量
Int32 lastIncrease = input[1] - input[0];
for (Int32 i = 1; i < input.Length; i++)
{
//序列增量
Int32 increase = input[i] - input[i - 1];
//如果序列与上一次序列不相同,或者序列自增绝对值非1。开始新的匹配。
if (increase != lastIncrease || Math.Abs(increase) != 1)
{
Int32 seriesLength = i - startIndex;
if (seriesLength > lastSeriesLength)
{
lastSeriesLength = seriesLength;
lastStartIndex = startIndex;
}
startIndex = i - 1;
lastIncrease = increase;
}
}
if (lastSeriesLength >= 3)
{
return input.Substring(lastStartIndex, lastSeriesLength);
}
else
{
return "";
}
}
baihacker 2009-08-13
  • 打赏
  • 举报
回复

//降序,长度至少为3
using System;
public class Test
{
public static int[,] dfa = new int[128,128];
public static void init()
{
for (int i = '1'; i <= '9'; ++i)
{
dfa[i,i-1] = i-1;
dfa[0,i] = i;
}
}
public static void match(String str)
{
int state = 0;
int last = 0;
for (int i = 0;; ++i)
{
if (i == str.Length)
{
if (i-last>=3)
{
for (int j = last; j < i; ++j) Console.Write("{0}", str[j]);
Console.WriteLine("");
}
break;
}
char c = str[i];
if (dfa[state,c] == 0)
{
if (i-last>=3)
{
for (int j = last; j < i; ++j) Console.Write("{0}", str[j]);
Console.WriteLine("");
}
if (c >= '0' && c <= '9')
{
state = c;last = i;
}
else
{
state = 0;last = i + 1;
}
}
else
{
state = dfa[state,c];
}
}
Console.WriteLine("");
}

static void Main()
{
init();
match("abc321abc123456789123465");
match("123a1bc321abc1234567891234656565");
match("9876543210,987654321,98765432,9876543,987654,98765,9876,987,876543210,87654321,8765432,876543,87654,8765,876,76543210,7654321,765432,76543,7654,765,6543210,654321,65432,6543,654,543210,54321,5432,543,43210,4321,432,3210,321,210");
}
}
baihacker 2009-08-13
  • 打赏
  • 举报
回复
[Quote=引用 33 楼 hweing 的回复:]
引用 30 楼 baihacker 的回复:
C# code//降序的...using System;publicclass Test
{publicstaticint[,] dfa=newint[128,128];publicstaticvoid init()
    {for (int i='1'; i <='9';++i)
        {
            dfa[i,i-1]= i-1;
            dfa[0,i]= i;
        }
    }publicstaticvoid match(String str)
    {int state=0;int last=0;for (int i=0;;++i)
        {if (i== str.Length)
            {if (i>last)
                {for (int j= last; j < i;++j) Console.Write("{0}", str[j]);
                    Console.WriteLine("");
                }break;
            }char c= str[i];if (dfa[state,c]==0)
            {if (i>last)
                {for (int j= last; j < i;++j) Console.Write("{0}", str[j]);
                    Console.WriteLine("");
                }if (c>='0'&& c <='9')
                {
                    state= c;last= i;
                }else
                {
                    state=0;last= i+1;
                }
            }else
            {
                state= dfa[state,c];
            }
        }
        Console.WriteLine("");
    }staticvoid Main()
    {
        init();
        match("abc123abc123456789123465");
        match("123a1bc123abc1234567891234656565");
        match("9876543210,987654321,98765432,9876543,987654,98765,9876,987,876543210,87654321,8765432,876543,87654,8765,876,76543210,7654321,765432,76543,7654,765,6543210,654321,65432,6543,654,543210,54321,5432,543,43210,4321,432,3210,321,210");
        }
}


执行了一下,得到的结果未到达预期效果。

[/Quote]
要改成三连续的很容易...只需要检查i和last的差
hweing 2009-08-13
  • 打赏
  • 举报
回复
[Quote=引用 30 楼 baihacker 的回复:]
C# code//降序的...using System;publicclass Test
{publicstaticint[,] dfa=newint[128,128];publicstaticvoid init()
{for (int i='1'; i<='9';++i)
{
dfa[i,i-1]= i-1;
dfa[0,i]= i;
}
}publicstaticvoid match(String str)
{int state=0;int last=0;for (int i=0;;++i)
{if (i== str.Length)
{if (i>last)
{for (int j= last; j< i;++j) Console.Write("{0}", str[j]);
Console.WriteLine("");
}break;
}char c= str[i];if (dfa[state,c]==0)
{if (i>last)
{for (int j= last; j< i;++j) Console.Write("{0}", str[j]);
Console.WriteLine("");
}if (c>='0'&& c<='9')
{
state= c;last= i;
}else
{
state=0;last= i+1;
}
}else
{
state= dfa[state,c];
}
}
Console.WriteLine("");
}staticvoid Main()
{
init();
match("abc123abc123456789123465");
match("123a1bc123abc1234567891234656565");
match("9876543210,987654321,98765432,9876543,987654,98765,9876,987,876543210,87654321,8765432,876543,87654,8765,876,76543210,7654321,765432,76543,7654,765,6543210,654321,65432,6543,654,543210,54321,5432,543,43210,4321,432,3210,321,210");
}
}
[/Quote]

执行了一下,得到的结果未到达预期效果。
baihacker 2009-08-13
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 hweing 的回复:]
22楼老兄是正解,正则做这个事情,不是很适合,经过测试发现自己写算法实现是正则表达式(考虑升序降序情况)的30倍性能,
以下是我写的提取手机号码升序或者降序的字符串(前提条件:手机号码只匹配最后8位,连续出现3次才匹配)
欢迎各位网友提出更好的算法。谢谢大家。

/// <summary>
        ///
        /// </summary>
        /// <param name="input"> </param>
        private String Match2(string input)
        {
            //第一个匹配的序列
            String increaseSeries1 = String.Empty;
            //第二个匹配的序列
            String increaseSeries2 = String.Empty;

            //最后一次匹配的起始位置
            Int32 startIndex = 0;
            Int32 seriesLength;
            //序列增量
            Int32 increase;
            //上一次序列增量
            Int32 lastIncrease = input[1] - input[0];
            for (Int32 i = 2; i < input.Length; i++)
            {
                increase = input[i] - input[i - 1];
                //如果序列与上一次序列不相同,或者序列自增绝对值非1。开始新的匹配。
                if (increase != lastIncrease || Math.Abs(increase) != 1)
                {
                    seriesLength = i - 1 - startIndex;
                    if (seriesLength >= 3)
                    {
                        increaseSeries1 = input.Substring(startIndex, seriesLength);
                    }
                    startIndex = i - 1;
                    lastIncrease = increase;
                }
            }
            seriesLength = input.Length - startIndex;
            if (seriesLength >= 3)
            {
                increaseSeries2 = input.Substring(startIndex, seriesLength);
            }
            if (increaseSeries2.Length > increaseSeries1.Length)
            {
                return increaseSeries2;
            }
            else
            {
                return increaseSeries1;
            }
        }
测试:
String[] inputs = "9876543210,987654321,98765432,9876543,987654,98765,9876,987,876543210,87654321,8765432,876543,87654,8765,876,
76543210,7654321,765432,76543,7654,765,6543210,654321,65432,6543,654,543210,54321,5432,543,43210,4321,432,3210,321,210"
.Split(',');

            Stopwatch watch = new Stopwatch();
            watch.Start();
            Int32 repeat = 0;
            while (repeat < 100000)
            {
                foreach (var num in inputs)
                {
                    if (num.Length < 3)
                    {
                        continue;
                    }

                    String result = this.Match2(num);
                    //sb.AppendFormat("{0}:\tInput\t{1}\t{2}\r\n", ++i, num, result);
                }
                repeat++;
            }
            watch.Stop();
            sb.AppendFormat("耗时:{0}(Ticks)\t{1}(毫秒)", watch.ElapsedTicks.ToString(),watch.ElapsedMilliseconds);

测试结果:
耗时:8846309(Ticks) 617(毫秒)

正则表达式的测试结果:
耗时:117673540(Ticks) 8218(毫秒)(只考虑降序,没有考虑升序情况).

再次感谢大家。
[/Quote]

dfa的结果何如?
要改成三连续的很容易...只需要检查i和last的差
hweing 2009-08-13
  • 打赏
  • 举报
回复
22楼老兄是正解,正则做这个事情,不是很适合,经过测试发现自己写算法实现是正则表达式(考虑升序降序情况)的30倍性能,
以下是我写的提取手机号码升序或者降序的字符串(前提条件:手机号码只匹配最后8位,连续出现3次才匹配)
欢迎各位网友提出更好的算法。谢谢大家。

/// <summary>
///
/// </summary>
/// <param name="input"></param>
private String Match2(string input)
{
//第一个匹配的序列
String increaseSeries1 = String.Empty;
//第二个匹配的序列
String increaseSeries2 = String.Empty;

//最后一次匹配的起始位置
Int32 startIndex = 0;
Int32 seriesLength;
//序列增量
Int32 increase;
//上一次序列增量
Int32 lastIncrease = input[1] - input[0];
for (Int32 i = 2; i < input.Length; i++)
{
increase = input[i] - input[i - 1];
//如果序列与上一次序列不相同,或者序列自增绝对值非1。开始新的匹配。
if (increase != lastIncrease || Math.Abs(increase) != 1)
{
seriesLength = i - 1 - startIndex;
if (seriesLength >= 3)
{
increaseSeries1 = input.Substring(startIndex, seriesLength);
}
startIndex = i - 1;
lastIncrease = increase;
}
}
seriesLength = input.Length - startIndex;
if (seriesLength >= 3)
{
increaseSeries2 = input.Substring(startIndex, seriesLength);
}
if (increaseSeries2.Length > increaseSeries1.Length)
{
return increaseSeries2;
}
else
{
return increaseSeries1;
}
}
测试:
String[] inputs = "9876543210,987654321,98765432,9876543,987654,98765,9876,987,876543210,87654321,8765432,876543,87654,8765,876,
76543210,7654321,765432,76543,7654,765,6543210,654321,65432,6543,654,543210,54321,5432,543,43210,4321,432,3210,321,210"
.Split(',');

Stopwatch watch = new Stopwatch();
watch.Start();
Int32 repeat = 0;
while (repeat < 100000)
{
foreach (var num in inputs)
{
if (num.Length < 3)
{
continue;
}

String result = this.Match2(num);
//sb.AppendFormat("{0}:\tInput\t{1}\t{2}\r\n", ++i, num, result);
}
repeat++;
}
watch.Stop();
sb.AppendFormat("耗时:{0}(Ticks)\t{1}(毫秒)", watch.ElapsedTicks.ToString(),watch.ElapsedMilliseconds);

测试结果:
耗时:8846309(Ticks) 617(毫秒)

正则表达式的测试结果:
耗时:117673540(Ticks) 8218(毫秒)(只考虑降序,没有考虑升序情况).

再次感谢大家。
baihacker 2009-08-13
  • 打赏
  • 举报
回复

//降序的...
using System;
public class Test
{
public static int[,] dfa = new int[128,128];
public static void init()
{
for (int i = '1'; i <= '9'; ++i)
{
dfa[i,i-1] = i-1;
dfa[0,i] = i;
}
}
public static void match(String str)
{
int state = 0;
int last = 0;
for (int i = 0;; ++i)
{
if (i == str.Length)
{
if (i>last)
{
for (int j = last; j < i; ++j) Console.Write("{0}", str[j]);
Console.WriteLine("");
}
break;
}
char c = str[i];
if (dfa[state,c] == 0)
{
if (i>last)
{
for (int j = last; j < i; ++j) Console.Write("{0}", str[j]);
Console.WriteLine("");
}
if (c >= '0' && c <= '9')
{
state = c;last = i;
}
else
{
state = 0;last = i + 1;
}
}
else
{
state = dfa[state,c];
}
}
Console.WriteLine("");
}

static void Main()
{
init();
match("abc123abc123456789123465");
match("123a1bc123abc1234567891234656565");
match("9876543210,987654321,98765432,9876543,987654,98765,9876,987,876543210,87654321,8765432,876543,87654,8765,876,76543210,7654321,765432,76543,7654,765,6543210,654321,65432,6543,654,543210,54321,5432,543,43210,4321,432,3210,321,210");
}
}
baihacker 2009-08-13
  • 打赏
  • 举报
回复

//不会C#,改得有点差,还算编译通过了...
using System;
public class Test
{
public static int[,] dfa = new int[128,128];
public static void init()
{
for (int i = '0'; i < '9'; ++i)
{
dfa[i,i+1] = i+1;
dfa[0,i] = i;
}
}
public static void match(String str)
{
int state = 0;
int last = 0;
for (int i = 0;; ++i)
{
if (i == str.Length)
{
if (i>last)
{
for (int j = last; j < i; ++j) Console.Write("{0}", str[j]);
Console.WriteLine("");
}
break;
}
char c = str[i];
if (dfa[state,c] == 0)
{
if (i>last)
{
for (int j = last; j < i; ++j) Console.Write("{0}", str[j]);
Console.WriteLine("");
}
if (c >= '0' && c <= '9')
{
state = c;last = i;
}
else
{
state = 0;last = i + 1;
}
}
else
{
state = dfa[state,c];
}
}
Console.WriteLine("");
}

static void Main()
{
init();
match("abc123abc123456789123465");
match("123a1bc123abc1234567891234656565");
}
}
rainsome 2009-08-13
  • 打赏
  • 举报
回复
不好意思.没有看懂"匹配某一组数字中含有的连续数字"的意思.

[Quote=引用 25 楼 hweing 的回复:]
引用 11 楼 rainsome 的回复:
C# code//Try it.string str=@"54321,98765,6543210,432,98745";

            Regex objRegex=new Regex(@"(? <=\D|^)(?:(?:9(?=8|\D))?(?:8(?=7|\D))?(?:7(?=6|\D))?(?:6(?=5|\D))?(?:5(?=4|\D))?(?:4(?=3|\D))?(?:3(?=2|\D))?(?:2(?=1|\D))?(?:1(?=0|\D))?(?:0(?=\D|$))?)(?=\D|$)");

            MatchCollection objMatches= objRegex.Matches(str);int i=0;foreach (Match min objMatches)
            {
                Response.Write(String.Format("Result[{0}]:{1} <br />",++i, m.Value));
            }/*
//Result:

Result[1]:54321
Result[2]:98765
Result[3]:6543210
Result[4]:432*/


不错,我测试了一下,很好。能够识别。
输入:
9876543210,987654321,98765432,9876543,987654,98765,9876,987,
876543210,87654321,8765432,876543,87654,8765,876,
76543210,7654321,765432,76543,7654,765,
6543210,654321,65432,6543,654,
543210,54321,5432,543,
43210,4321,432,
3210,321,
210
测试结果:
1:9876543210
2:987654321
3:98765432
4:9876543
5:987654
6:98765
7:9876
8:987
9:876543210
10:87654321
11:8765432
12:876543
13:87654
14:8765
15:876
16:76543210
17:7654321
18:765432
19:76543
20:7654
21:765
22:6543210
23:654321
24:65432
25:6543
26:654
27:543210
28:54321
29:5432
30:543
31:43210
32:4321
33:432
34:3210
35:321
36:210
可是我想要匹配某一组数字中含有的连续数字。我该怎么办?这样写的效率如何?
如果要匹配降序的连续数字,岂不是又要执行一次?
[/Quote]



//修正下bug
Regex objRegex = new Regex(@"(?<=\D|^)(?:(?:9(?=8|\D|$))?(?:8(?=7|\D|$))?(?:7(?=6|\D|$))?(?:6(?=5|\D|$))?(?:5(?=4|\D|$))?(?:4(?=3|\D|$))?(?:3(?=2|\D|$))?(?:2(?=1|\D|$))?(?:1(?=0|\D|$))?(?:0(?=\D|$))?)(?=\D|$)");



[Quote=引用 22 楼 lxcnn 的回复:]
引用 14 楼 rainsome 的回复:
Regex objRegex=new Regex(@"(? <=\D|^)(?:(?:9(?=8|\D))?(?:8(?=7|\D))?(?:7(?=6|\D))?(?:6(?=5|\D))?(?:5(?=4|\D))?(?:4(?=3|\D))?(?:3(?=2|\D))?(?:2(?=1|\D))?(?:1(?=0|\D))?(?:0(?=\D|$))?)(?=\D|$)");


又考虑了下,这个问题用正则来解决终究还是不可取的

14楼的实现上有bug,比如
string str = @"98765";
是匹配不到任何值的

但即使修改了bug,需要考虑的情况也是比较多,不适合用一个正则来解决,如果非要用正则,可以用正则来提取数字子串,再通过自己写逻辑来验证是否连续的数字

其实这种需求不用正则更好一些,直接写逻辑也不会是多困难的事
[/Quote]

hweing 2009-08-13
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 baihacker 的回复:]
C/C++ code//用C++写的用有限自动机解决这个匹配的程序.//希望对你有帮助.
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>usingnamespace std;int dfa[128][128];void init()
{for (int i='0'; i<'9';++i) dfa[i][i+1]= i+1, dfa[0][i]= i;
}void match(constchar* str)
{int state=0;int last=0;for (int i=0;;++i)
{char c= str[i];if (!c)
{if (i>last)
{for (int j= last; j< i;++j) putchar(str[j]);
puts("");
}break;
}if (dfa[state][c]==0)
{if (i>last)
{for (int j= last; j< i;++j) putchar(str[j]);
puts("");
}if (isdigit(c)) state= c, last= i;else state=0, last= i+1;
}else
{
state= dfa[state][c];
}
}
puts("");
}int main()
{
init();
match("abc123abc123456789123465");
match("123a1bc123abc1234567891234656565");return0;
}
[/Quote]

这位大哥的算法我没有看懂,能否简单解释一下“有限自动机”,有谁能够把C++代码翻译成C#代码,谢谢。
liuhong_0325 2009-08-13
  • 打赏
  • 举报
回复
哇 学习了!
hweing 2009-08-13
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 rainsome 的回复:]
C# code//Try it.string str=@"54321,98765,6543210,432,98745";

Regex objRegex=new Regex(@"(?<=\D|^)(?:(?:9(?=8|\D))?(?:8(?=7|\D))?(?:7(?=6|\D))?(?:6(?=5|\D))?(?:5(?=4|\D))?(?:4(?=3|\D))?(?:3(?=2|\D))?(?:2(?=1|\D))?(?:1(?=0|\D))?(?:0(?=\D|$))?)(?=\D|$)");

MatchCollection objMatches= objRegex.Matches(str);int i=0;foreach (Match min objMatches)
{
Response.Write(String.Format("Result[{0}]:{1}<br />",++i, m.Value));
}/*
//Result:

Result[1]:54321
Result[2]:98765
Result[3]:6543210
Result[4]:432*/
[/Quote]

不错,我测试了一下,很好。能够识别。
输入:
9876543210,987654321,98765432,9876543,987654,98765,9876,987,
876543210,87654321,8765432,876543,87654,8765,876,
76543210,7654321,765432,76543,7654,765,
6543210,654321,65432,6543,654,
543210,54321,5432,543,
43210,4321,432,
3210,321,
210
测试结果:
1:9876543210
2:987654321
3:98765432
4:9876543
5:987654
6:98765
7:9876
8:987
9:876543210
10:87654321
11:8765432
12:876543
13:87654
14:8765
15:876
16:76543210
17:7654321
18:765432
19:76543
20:7654
21:765
22:6543210
23:654321
24:65432
25:6543
26:654
27:543210
28:54321
29:5432
30:543
31:43210
32:4321
33:432
34:3210
35:321
36:210
可是我想要匹配某一组数字中含有的连续数字。我该怎么办?这样写的效率如何?
如果要匹配降序的连续数字,岂不是又要执行一次?
baihacker 2009-08-13
  • 打赏
  • 举报
回复
[Quote=引用 36 楼 hweing 的回复:]
经过测试发现,DFA的性能很差,使用的是帖子中的C#版本算法。
执行单个测试耗时:耗时:14162(Ticks),
正则表达式单个耗时:1176Ticks.

不知道是不是C++转换成C#造成的性能损失还是算法本身的性能问题。

[/Quote]
不清楚C#怎么写高效.
正则式引擎里面还不是构造NFA,然后转DFA,然后化简状态...
还要注意输出,我那样的写法明显很低效.
在测试不仅要注意算法本身,还要注意非算法因素.
hweing 2009-08-13
  • 打赏
  • 举报
回复
经过测试发现,DFA的性能很差,使用的是帖子中的C#版本算法。
执行单个测试耗时:耗时:14162(Ticks),
正则表达式单个耗时:1176Ticks.

不知道是不是C++转换成C#造成的性能损失还是算法本身的性能问题。
yuji821 2009-08-12
  • 打赏
  • 举报
回复
fffffff
baihacker 2009-08-12
  • 打赏
  • 举报
回复
用DFA的方法我已经示范过了...只需要O(n)的扫描.只示例了上升的,下降的可以再扫描一遍.
-过客- 2009-08-12
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 rainsome 的回复:]
            Regex objRegex=new Regex(@"(? <=\D|^)(?:(?:9(?=8|\D))?(?:8(?=7|\D))?(?:7(?=6|\D))?(?:6(?=5|\D))?(?:5(?=4|\D))?(?:4(?=3|\D))?(?:3(?=2|\D))?(?:2(?=1|\D))?(?:1(?=0|\D))?(?:0(?=\D|$))?)(?=\D|$)");           
[/Quote]

又考虑了下,这个问题用正则来解决终究还是不可取的

14楼的实现上有bug,比如
string str = @"98765";
是匹配不到任何值的

但即使修改了bug,需要考虑的情况也是比较多,不适合用一个正则来解决,如果非要用正则,可以用正则来提取数字子串,再通过自己写逻辑来验证是否连续的数字

其实这种需求不用正则更好一些,直接写逻辑也不会是多困难的事
zhongjiekangping 2009-08-12
  • 打赏
  • 举报
回复
这个用正则?
wuyq11 2009-08-12
  • 打赏
  • 举报
回复
正则用于这方面应该不可取,通过分分割字符串,判断是否连续
加载更多回复(19)

110,548

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧