繁体   English   中英

在 C# 中第一次出现没有正则表达式的字母时拆分字符串

[英]Split string at first occurence of a letter without regex in C#

我有很多字符串,它们都以 CIDR IP 地址开头(例如 192.168.1.104),但其中一些以随机字母结尾(例如 192.168.1.104kadjwneqb)。 有没有办法在不使用正则表达式的情况下在字母的第一次出现时拆分这些字符串? 正则表达式计算量太大,因为我需要处理很多这些。 先感谢您

根据我的测试,自定义循环比您使用的正则表达式快五倍左右(尽管可能有可能会快一点的正则表达式)。

我使用 BenchmarkDotNet 进行了测试,结果是:

|                                Method |      Mean |     Error |    StdDev |
|-------------------------------------- |----------:|----------:|----------:|
| BenchTruncateAtLastDigitViaCustomCode |  4.846 ms | 0.0531 ms | 0.0652 ms |
|      BenchTruncateAtLastDigitViaRegex | 21.886 ms | 0.2421 ms | 0.2265 ms |

和测试代码:

using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using BenchmarkDotNet.Attributes;

namespace CoreConsoleA
{
    public class UnderTest
    {
        public UnderTest()
        {
            var rng = new Random(12456); // Seeded RNG, so same data every run.

            // Test with 100,000 IP addresses of which 10% have between 1 and 20 extra characters.

            var data = new byte[4]; 
            int n = 100_000;

            for (int i = 0; i < n; ++i)
            {
                rng.NextBytes(data); // Create a random 
                IPAddress ipAddress = new IPAddress(data);

                if (rng.NextDouble() < 0.1)
                {
                    int extra = rng.Next(1, 21);
                    _strings.Add(ipAddress + new string('x', extra));
                }
                else
                {
                    _strings.Add(ipAddress.ToString());
                }
            }
        }

        [Benchmark]
        public void BenchTruncateAtLastDigitViaCustomCode()
        {
            foreach (var s in _strings)
            {
                TruncateAtLastDigitViaCustomCode(s);
            }
        }

        [Benchmark]
        public void BenchTruncateAtLastDigitViaRegex()
        {
            foreach (var s in _strings)
            {
                TruncateAtLastDigitViaRegex(s);
            }
        }

        public string TruncateAtLastDigitViaCustomCode(string s)
        {
            return s.Substring(0, IndexOfLastDigitViaCustomCode(s));
        }

        public string TruncateAtLastDigitViaRegex(string s)
        {
            return s.Substring(0, IndexOfLastDigitViaRegex(s));
        }

        public static int IndexOfLastDigitViaCustomCode(string s)
        {
            for (int i = 0; i < s.Length; ++i)
            {
                char c = s[i];

                if (!char.IsDigit(c) && c != '.')
                    return i;
            }

            return s.Length;
        }

        public int IndexOfLastDigitViaRegex(string s)
        {
            int index = _ipTruncate.Match(s).Index;

            return index > 0 ? index : s.Length;
        }

        readonly Regex _ipTruncate = new Regex("[^0-9.]", RegexOptions.Compiled);

        readonly List<string> _strings = new List<string>();
    }
}

有些人在遇到问题时会想“我知道,我会使用正则表达式”。 现在他们有两个问题。

几条评论声称正则表达式应该没问题,所以我进行了测试。 在发布模式下编译,在我的机器上,正则表达式方法慢 10 倍。 可能仍然可以,这取决于当然的上下文。 但是 IMO 天真的实现(它只是在字符串中查找非数字、非小数点字符,然后返回一个子字符串)也更容易理解。 天啊。

static class Program
{
    static void Main(string[] args)
    {
        var input = "192.168.1.104kadjwneqb";
        Timed(() => GetIp1(input));
        Timed(() => GetIp2(input));
    }
    static Regex regex = new Regex(@"^(?<cidr>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})[a-zA-Z]*$", RegexOptions.Compiled);

    static void Timed(Action a)
    {
        var sw = Stopwatch.StartNew();
        for (int i = 0; i < 10_000_000; i++)
            a();
        Console.WriteLine(sw.ElapsedMilliseconds);
    }

    static string GetIp1(string input)
    {
        int i = 0;
        while (char.IsDigit(input[i]) || input[i] == '.') i++;
        return input.Substring(0, i);
    }
    static string GetIp2(string input)
    {
        var m = regex.Match(input);
        return m.Groups["cidr"].Value;
    }
}

不使用正则表达式或循环来做到这一点的最简单方法可能是这样的:

using System;

public class Program
{
    public static void Main()
    {
        string inputIp="192.168.12.127fjieif34f";

        int firstNumber = inputIp.IndexOfAny("0123456789".ToCharArray());
        int firstAlpha = inputIp.IndexOfAny("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".ToCharArray(), firstNumber);

        string ip = inputIp.Substring(firstNumber, firstAlpha - firstNumber);

        Console.WriteLine("The IP is " + ip);
    }
}

这是它的工作原理:

  1. 它找到第一个数字的索引(因为 ip 地址以数字开头)
  2. 然后它找到第一个字母字符的索引。
  3. 它使用Substring提取位于firstNumberfirstAlpha之间的 ip。

这个简单的例子没有做任何你可能想要做的检查(比如检查IndexOfAny的返回值)。

暂无
暂无

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

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