【问题标题】:How to get Nth number from the specific position of a number in a string - Regex如何从字符串中数字的特定位置获取第 N 个数字 - 正则表达式
【发布时间】:2014-09-07 07:56:23
【问题描述】:
288007  327920  374740 000368   044575  082865 680798
717374  755879  811106  855460  920577  953515  996819 ......

我有一个包含数千个 6 位数字的字符串,我想借助正则表达式提取第 N 个数字之后的第 N 个数字。

假设我需要在第四个数字之后提取三个数字,那么结果应该是044575 082865 680798

另一个例子如果我需要在第 10 个数字之后提取 2 个数字,那么结果应该是 855460 920577

我不知道正则表达式是否可行,我认为FOR EACH 语句可能适用于我的情况。

我只能使用下面的代码提取每个六位数字。

Dim NumberMatchCollection As MatchCollection = Regex.Matches("String containing numbers", "(?<!\d)\d{6}(?!\d)")
For Each NumberMatch As Match In NumberMatchCollection

   Dim ItemNumber As String = NumberMatch.Value

Next

编辑: 我不能保证每个分隔符都是一个空格、一个双空格、一个制表符或其他东西。我可以保证数字长度始终为 6,由空格或制表符分隔。

【问题讨论】:

  • 为什么特别是正则表达式而不是String.Split
  • Regex 不是必需的,任何其他建议和方法将不胜感激。
  • 在您的示例中,某些数字未按列排列,例如 000368680798 向左移动了一个字符。这是代表您的真实数据还是只是拼写错误?
  • @ErikE,这是我的真实数据,数字是否必须在列中?
  • @ErikE 我不能保证每个分隔符都是一个空格、一个双空格、一个制表符或其他东西。我可以保证数字长度始终为 6,由一些空格或制表符分隔。

标签: c# regex vb.net


【解决方案1】:

这不是用数学更简单吗?

第四个数字后面的三个数字,是chars (7 * 4) + (7 * 3)

【讨论】:

  • 我没听懂你的回答,你能探索一下这个解决方案吗?谢谢你的回答。
  • 如果每个数字有 6 位数字,中间有一个空格。那么第 4 个数字的位置将是 (6+1)*4,如果你想要 3 个数字而不是你只需要获取 (6+1)*3 个字符。
  • @the_lotus - 您对 1 个空格的假设与 OP 提供的示例数据不匹配
  • @the_lotus 是的,你是对的。我不确定数字分隔符,这也可以是Tab 或四个空格。
【解决方案2】:

扩展我的评论。这假设实际数据被平均分配。

如果每个数字有 6 位数字,中间有一个空格。那么第 4 个数字的位置将是 (6+1)*4,如果你想要 3 个数字,你只需要获取 (6+1)*3 个字符。

    Dim str As String

    str = "288007 327920 374740 000368 044575 082865 680798 717374 755879 811106 855460 920577 953515 996819"

    Dim startingNumber As Integer = 4
    Dim amountToFetch As Integer = 3

    ' 7 = [size of each number] + [delimiter length]
    ' 7 = 6 + 1

    Console.WriteLine(str.Substring(7 * startingNumber, 7 * amountToFetch))
    Console.ReadLine()

【讨论】:

  • 实际上,我不想依赖分隔符(即空格或制表符等),因为我不确定每次分隔符都是一个空格,这就是我写一个正则表达式匹配字符串中的每个 6 位数字。
【解决方案3】:

如果您想要正则表达式和 c# 解决方案,以下代码将执行第 4 个数字示例之后的 3 个数字。

        var st = @"288007  327920  374740 000368   044575  082865 680798
                  717374  755879  811106  855460  920577  953515  996819";
        var pattern = @"^(\d+\s+){4}((?<x>\d+)\s+){3}";
        var matches = Regex.Matches(st,pattern,RegexOptions.Singleline);
        foreach (Capture m in matches[0].Groups["x"].Captures)
            Console.WriteLine("value={0}", m.Value);

(编辑:每条评论删除一组)

【讨论】:

  • 您可能可以消除两个捕获组,因为它对于已经捕获的内容是多余的。 ^(\d+\s+){4}(?&lt;x&gt;\d+\s+){3} — 否则你的模式可以正常工作。
  • 我包含了额外的组,以便 Capture 可以循环通过以单独获取每个数字。是的,你的得到了 3 个数字,但它们被捕获为带有尾随空格的空格分隔字符串。
  • 不确定我是否关注,这里是your original patternthe one I suggested;没有必要在每个捕获组中重复具有相同编号的三个捕获组。
  • 对不起,我的错,你的也有 3 个单独的数字,只有你的数字后面会有空格。我的目的是消除尾随空格。是的,我的组太多了。 S/b ^(\d+\s+){4}((?&lt;x&gt;\d+)\s+){3}
  • 无论如何我都会将结果字符串转换为整数,这将处理匹配中显示的空格,并消除 OP 在问题中展示的不稳定间距的潜在问题。 :)
【解决方案4】:

您可以 .Split() 字符串并在结果数组上使用 LINQ 扩展方法:

// some test data...
var rand = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 10000; i++)
{
    sb.Append(i.ToString("000000") + ((rand.Next(5)==1) ? "  ": "\t"));
}
string s = sb.ToString();

string portion = string.Join("  ", s.Split(new [] {' ', '\t'}, StringSplitOptions.RemoveEmptyEntries).Skip(10).Take(3));

Console.WriteLine(portion); // outputs "000011  000012  000013"

注意:对于第一个号码,您可以.Skip(0)

但是如果你的字符串是你显示的严格格式(假设可变数量的空格是拼写错误,感谢@ErikE),Coenraad 计算所需字符串的开头在哪里以及要占用多少个字符的方法会更有效率。我将把它留给 Coenraad 来扩展这个答案,因为可能拿分是不公平的。

我尝试并尝试使正则表达式方法始终保持快速,但我发现它在很大程度上取决于您要检索的数字:

对于任何想要测试的人,我在表单上放置了一个默认图表并使用了以下代码:

Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Windows.Forms.DataVisualization
Imports System.Windows.Forms.DataVisualization.Charting

Public Class Form1

    Sub DoStuff()

        Dim ser1 As New Series With {.Name = "String.Split"}
        Dim ser2 As New Series With {.Name = "RegEx"}

        Dim sb As New StringBuilder()

        For i As Integer = 1 To 10000
            sb.Append(i.ToString("000000") + "  ")
        Next
        Dim s As String = sb.ToString()

        Dim sw As New Stopwatch()

        Dim itemsToTake As Integer = 50

        For firstItem = 1 To 9000 Step 100

            sw.Restart()

            Dim portion As String = String.Join(" ", s.Split({" "c}, StringSplitOptions.RemoveEmptyEntries).Skip(firstItem - 1).Take(itemsToTake))

            sw.Stop()
            ser1.Points.AddXY(firstItem -1, sw.ElapsedTicks)

            Dim pattern = "^(?:\d+\s+){" + (firstItem - 1).ToString() + "}((\d+)\s+){" + itemsToTake.ToString() + "}"
            Dim re = New Regex(pattern)

            sw.Restart()
            Dim matches = re.Matches(s)
            Dim cs = matches(0).Groups(0).Captures
            sw.Stop()
            ser2.Points.AddXY(firstItem - 1, sw.ElapsedTicks)

        Next

        Chart1.Series.Clear()
        Chart1.Series.Add(ser1)
        Chart1.Series(0).ChartType = SeriesChartType.Line
        Chart1.Series.Add(ser2)
        Chart1.Series(1).ChartType = SeriesChartType.Line

        Chart1.ChartAreas(0).AxisX.IsMarginVisible = False
        Chart1.ChartAreas(0).AxisX.Title = "First item to retrieve"
        Chart1.ChartAreas(0).AxisY.Title = "Time taken"

    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        DoStuff()

    End Sub

End Class

【讨论】:

  • 感谢您的回答,您的回答清晰易懂。它看起来像一段工作代码。我要试试你的解决方案。
猜你喜欢
  • 2021-09-12
  • 2014-04-09
  • 1970-01-01
  • 2016-12-09
  • 1970-01-01
  • 2021-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多