【问题标题】:Obtain next "version" (.NET Regex)获取下一个“版本”(.NET Regex)
【发布时间】:2012-09-28 14:20:45
【问题描述】:

我需要从字符串version(最多 3 个字符,如 sql varchar(3),“下一个版本”,具有以下简单规则:

如果它的最后一个字符构成一个数字,则递增它,如果它总是最多容纳 3 个字符,否则,保持原样。

说 "Hi1" => "Hi2", "H9" => "H10" 但 "xx9" 将保持不变。

  Public Shared Function GetNextVersion(oldVersion As String) As String
    Dim newVersion As String = String.Empty
    If Regex.IsMatch(oldVersion, "?????") Then
      Return newVersion
    Else
      Return oldVersion
    End If
  End Function

【问题讨论】:

  • 这很容易按照您的描述逐字编写......而且我只看到需要使用正则表达式将字符串拆分为字母和数字部分,而不是匹配。跨度>
  • 未来参考请阅读homework标签说明

标签: c# .net regex vb.net


【解决方案1】:

这将是您的正则表达式,它将匹配任何带有一组字母和一组数字的内容。

Public Dim regex As Regex = New Regex( _
      "^(?<prefix>.*?0*)(?<version>\d+)$", _
      RegexOptions.IgnoreCase _
        Or RegexOptions.CultureInvariant _
        Or RegexOptions.Compiled _
    )

它将包含两个命名的捕获组,“前缀”是初始字符,“版本”是您的版本。

将版本转换为 int,递增,并通过连接前缀和新版本返回新版本号。

所以,你最终会得到这样的结果

Public versionRegex As Regex = New Regex( _
   "^(?<prefix>.*?0*)(?<version>\d+)$", _
   RegexOptions.IgnoreCase _
     Or RegexOptions.CultureInvariant _
     Or RegexOptions.Compiled _
 )

Public Shared Function GetNextVersion(oldVersion As String) As String
    Dim matches = versionRegex.Matches(oldVersion)
    If (matches.Count <= 0) Then
        Return oldVersion
    End If

    Dim match = matches(0)
    Dim prefix = match.Groups.Item("prefix").Value
    Dim version = CInt(match.Groups.Item("version").Value)

    Return String.Format("{0}{1}", prefix, version + 1)
End Function

【讨论】:

  • 这不适用于“7x7”,也不适用于“k*7”...,但这不太重要,任何验证长度都没有完成...
  • @serhio,我怎么知道这是一个有效的版本号?给我们一些例子,输入和输出
  • 我在问题中提供了一些示例。更多示例:“7x7”=>“7x8”、“1c”=>“1c”、“11”=>“12”、“*0”=>“*1”
  • @serhio,现在试试,我将正则表达式更新为^(?&lt;prefix&gt;.*?)(?&lt;version&gt;\d+)$,它现在将版本作为字符串右侧的数字返回,最后一批数字之前的任何内容都是考虑前缀。
  • @serhio,你不会看到它,因为它是一个非打印字符,但是,换行符不是数字的一部分,所以不要将它作为数字的一部分捕获。跨度>
【解决方案2】:

我不会使用正则表达式
正则表达式是一个解析器,这比解析更符合逻辑
对于 3 个字符,正则表达式并不比蛮力快

战略就是绩效。
必须测试边缘情况,而且有很多。
先做便宜的东西。
显然这是 C#

Think 得到了所有的测试用例。

static void Main(string[] args)
    {
        System.Diagnostics.Debug.WriteLine(NewVer("H"));
        System.Diagnostics.Debug.WriteLine(NewVer("Hi"));
        System.Diagnostics.Debug.WriteLine(NewVer("Hii"));
        System.Diagnostics.Debug.WriteLine(NewVer("Hiii"));
        System.Diagnostics.Debug.WriteLine(NewVer("H1"));
        System.Diagnostics.Debug.WriteLine(NewVer("H9"));
        System.Diagnostics.Debug.WriteLine(NewVer("Hi1"));
        System.Diagnostics.Debug.WriteLine(NewVer("H19"));
        System.Diagnostics.Debug.WriteLine(NewVer("9"));
        System.Diagnostics.Debug.WriteLine(NewVer("09"));
        System.Diagnostics.Debug.WriteLine(NewVer("009"));
        System.Diagnostics.Debug.WriteLine(NewVer("7"));
        System.Diagnostics.Debug.WriteLine(NewVer("07"));
        System.Diagnostics.Debug.WriteLine(NewVer("27"));
        System.Diagnostics.Debug.WriteLine(NewVer("347"));
        System.Diagnostics.Debug.WriteLine(NewVer("19"));
        System.Diagnostics.Debug.WriteLine(NewVer("999"));
        System.Diagnostics.Debug.WriteLine(NewVer("998"));
        System.Diagnostics.Debug.WriteLine(NewVer("C99"));
        System.Diagnostics.Debug.WriteLine(NewVer("C08"));
        System.Diagnostics.Debug.WriteLine(NewVer("C09"));
        System.Diagnostics.Debug.WriteLine(NewVer("C11"));
    }

    public static string NewVer(string oldVer)
    {
        string newVer = oldVer.Trim();
        if (string.IsNullOrEmpty(newVer)) return oldVer;
        if (newVer.Length > 3) return oldVer;
        // at this point all code paths need char by postion
        // regex is not the appropriate tool
        Char[] chars = newVer.ToCharArray();
        if (!char.IsDigit(chars[chars.Length - 1])) return oldVer;
        byte lastDigit = byte.Parse(chars[chars.Length - 1].ToString());
        if (lastDigit != 9)
        {
            lastDigit++;
            StringBuilder sb = new StringBuilder();
            for (byte i = 0; i < chars.Length - 1; i++)
            {
                sb.Append(chars[i]);
            }
            sb.Append(lastDigit.ToString());
            return sb.ToString();
        }
        // at this point the last char is 9  and lot of edge cases 
        if (chars.Length == 1) return (lastDigit + 1).ToString();
        if (char.IsDigit(chars[chars.Length - 2]))
        {
            if (chars.Length == 2) return ((byte.Parse(newVer)) + 1).ToString();
            byte nextToLastDigit = byte.Parse(chars[chars.Length - 2].ToString());
            if (nextToLastDigit == 9)
            {
                if (char.IsDigit(chars[0]))
                {
                    byte firstOfthree = byte.Parse(chars[0].ToString());
                    if (firstOfthree == 9) return oldVer; // edge case 999
                    // all three digtis and not 999
                    return ((byte.Parse(newVer)) + 1).ToString();
                }
                // have c99
                return oldVer;
            }
            else
            {
                //have c 1-8 9 
                return chars[0].ToString() + (10 * nextToLastDigit + lastDigit + 1).ToString();        
            }
        }
        // at this point have c9 or cc9
        if (chars.Length == 3) return oldVer;
        // at this point have c9
        return chars[0].ToString() + "10";
    }

【讨论】:

  • ...天哪!...“可读性第一”的原则怎么样?
  • 它是为性能而写的。你没明白我所说的“先做便宜的东西”吗?它有 15 行长 - 你不遵循哪一行?正则表达式对于获取数字的最后一个字符测试来说太过分了。接受的解决方案不测试长度或 9。
  • @serhio 你批评了我的回答,但它从 x02 返回 x03
  • 增加版本号时性能不是问题,除非您将其增加一百万次。
  • @OlivierJacot-Descombes 不知道它不会被执行一百万次。如果它只完成快 100 倍不会破坏它。
猜你喜欢
  • 1970-01-01
  • 2017-10-26
  • 1970-01-01
  • 2011-02-13
  • 1970-01-01
  • 1970-01-01
  • 2018-05-02
  • 2020-06-19
  • 2018-07-12
相关资源
最近更新 更多