需要一些正则表达式帮助

本文关键字:正则表达式 帮助 | 更新日期: 2023-09-27 18:15:11

尝试匹配包含在一个字符串中的此数据:

vsan 1 interfaces:
fc2/1             
vsan 10 interfaces:
fc1/1             fc1/2             fc1/3             fc1/4             
fc1/5             fc1/6             fc1/7             fc1/8             
fc1/9             fc1/10            fc1/11            fc1/12            
fc1/13            fc1/14            fc1/15            fc1/16    

我得到的输出按每个vsan正确分组,但我只得到每个vsan中的第一个接口(fcnn/nn(。例如,在vsan 10中,我想要所有的接口,但我只得到fc1/1。
这是我正在使用的Regex:

string MemberMatchString = 
    @"vsan's(?<number>'d+)['s]interfaces:'n's+(?<interfaces>'sfc'd+/'d+)'s+'n?";
MatchCollection MemberList = Regex.Matches(block, MemberMatchString);

需要一些正则表达式帮助

根据parapura的建议,我会使用String.Split(),至少用于检索接口:

String block = "vsan 10 interfaces:'nfc1/1             fc1/2   fc1/3'nfc1/4";
String number = Regex.Match(block, @"vsan's(?<number>'d+)'sinterfaces:").
    Groups["number"].Value;
String[] interfaces = block.Substring(block.IndexOf(':') + 2).
    Split(new char[] {' '}, StringSplitOptions.RemoveEmptyEntries);

您只得到了第一个界面,因为您的regex请求匹配(简化(:

vsan X interfaces:
fcX/X

这意味着您希望vsan X interfaces:出现在每个fcX/X的前面,而字符串中的情况并非如此。

它认为最好的解决方案是使用Tokenizer/Paser来获取您想要的信息。
我不认为你可以用一个Regex来处理它(对其他人来说是高效和透明的(。在我看来,Regex并不总是复杂字符串问题的解决方案

我还认为,大型Regex在任何时候都是不可维护的,因此应该避免。(自己试试,从互联网上的任何页面获取任何复杂的Regex。不要阅读表达式应该做什么,试着描述表达式的功能。你会对作者的期望和表达式的作用感到满意。

简单的Regex以粗体显示我的观点:

这个Regex是干什么的?

(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:'.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:['x01-'x08'x0b'x0c'x0e-'x1f'x21'x23-'x5b'x5d-'x7f]|''['x01-'x09'x0b'x0c'x0e-'x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?'.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|'[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:['x01-'x08'x0b'x0c'x0e-'x1f'x21-'x5a'x53-'x7f]|''['x01-'x09'x0b'x0c'x0e-'x7f])+)'])

此Regex是此页面中RFC 2822的实现。

这个是同一页的缩小版。

[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:'.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?'.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?

两者都可用于检查电子邮件地址!你预料到了吗?

解决方案:

创建一个Tokenzier/Parser:所以为了向你展示我将如何解决这个问题,我为你准备了一个工作示例
只需将其复制粘贴到控制台应用程序,就可以正常工作:-(
请给我关于我的代码的反馈,以及我的帮助是否对你有用。

示例说明:

创建一个Tokenizer,它将表达式拆分为匹配的令牌。在这种情况下,令牌源自接口IToken
令牌是由Line创建的,而不是由与组匹配的regex创建的。参见方法TokenizeTokenizeLine。他们做艰苦的工作。

  1. 拆分(直线和空格(
  2. 匹配(如果是Vsan或Interface(


拆分后,它变得非常简单。只需在Tokens中前进并创建VSanInterface对象
令牌的顺序在字符串组(由Tokenize中的Regex定义(的顺序之后,因此在列表中移动总是:

VSan
   Interface
   Interface
   Interface
Vsan
   Interface
   Interface

因此,您可以轻松地创建一个VSan对象,然后添加所有接口。如果下一个VSan令牌正在排队,只需创建一个新的VsanObject,并继续向新实例添加接口。

现在您有一个Vsans列表,其中包含一个可以使用的接口列表
我覆盖了ToString方法以使结果可见。

示例代码:

public static class Program{
        static string VsanString = 
@"vsan 1 interfaces:
fc2/1             
vsan 10 interfaces:
fc1/1             fc1/2             fc1/3             fc1/4             
fc1/5             fc1/6             fc1/7             fc1/8             
fc1/9             fc1/10            fc1/11            fc1/12            
fc1/13            fc1/14            fc1/15            fc1/16    ";
        public static void Main (string[] args) {
            VSanTokenizer vSanTokenizer = new VSanTokenizer(VsanString);
            IList<IToken> tokens = vSanTokenizer.Tokens;
            VsanTokenInterpreter vsanTokenInterpreter = new VsanTokenInterpreter(tokens);
            IList<IVSan> vSans = vsanTokenInterpreter.VSans;
            foreach (IVSan vSan in vSans) {
                Console.WriteLine(vSan.ToString());
            }
            Console.WriteLine("Please press return to quit.");
            Console.ReadLine();
        }
    }
    interface IVSan {
        int Number { get; }
        IList<IInterface> Interfaces { get; }
    }
    class VSan : IVSan {
        private readonly IList<IInterface> interfaces;
        private readonly int number;
        public VSan(int number, IList<IInterface> interfaces) {
            this.number = number;
            this.interfaces = interfaces;
        }
        public override string  ToString() {
            StringBuilder toString = new StringBuilder();
            toString.Append("Vsan with Number: ");
            toString.Append(number);
            toString.Append(" has following Interfaces:");
            toString.AppendLine("");
            foreach (IInterface @interface in Interfaces) {
                toString.Append("Intefaces with Name: ");
                toString.Append(@interface.Name);
                toString.AppendLine("");
            }
            return toString.ToString();
        }
        #region Implementation of IVSan
        public int Number {
            get { return number; }
        }
        public IList<IInterface> Interfaces {
            get { return interfaces; }
        }
        #endregion
    }

    interface IInterface {
        string Name { get; }
    }
    class Interface : IInterface {
        private readonly string name;
        public Interface(string name) {
            this.name = name;
        }
        #region Implementation of IInterface
        public string Name {
            get { return name; }
        }
        #endregion
    }
    interface IToken {
        string Value { get; }
    }
    interface IVsanToken : IToken {
        int VsanInterfaceNumber { get; }
    }
    internal abstract class AbstractToken : IToken {
        private readonly string value;
        public AbstractToken(string value) {
            this.value = value;
        }
        #region Implementation of IToken
        public string Value {
            get { return value; }
        }
        #endregion
    }
    class VsanToken : AbstractToken, IVsanToken {
        private readonly int vsanInterfaceNumber;
        public VsanToken(string value)
            : base(value) {
            vsanInterfaceNumber = int.Parse(value);
        }
        #region Implementation of IVsanToken
        public int VsanInterfaceNumber {
            get { return vsanInterfaceNumber; }
        }
        #endregion
    }
    class InterfaceToken : AbstractToken, IInterfaceToken {
        private readonly int firstNumber;
        private readonly int secondNumber;
        public InterfaceToken(string value)
            : base(value) {
            Match match = Regex.Match(value, "fc([0-9])/([0-9]+)");
            Group firstNumberGroup = match.Groups[1];
            Group secondNumberGroup = match.Groups[2];
            firstNumber = int.Parse(firstNumberGroup.Value);
            secondNumber = int.Parse(secondNumberGroup.Value);
        }
        public int SecondNumber {
            get { return secondNumber; }
        }
        public int FirstNumber {
            get { return firstNumber; }
        }
    }
    interface IInterfaceToken : IToken {
        //Edited: Added Second and FirstNumber to Interface so it can be accessed
        int SecondNumber { get; }
        int FirstNumber { get ; }
    }
    class VSanTokenizer {
        private readonly string vSanString;
        private IList<IToken> tokens;
        public VSanTokenizer(string vSanString) {
            this.vSanString = vSanString;
            tokens = Tokenize(vSanString);
        }
        public string VSanString {
            get { return vSanString; }
        }
        private IList<IToken> Tokenize(string vSanString) {
            List<IToken> tokens = new List<IToken>();
            StringReader reader = new StringReader(vSanString);
            string readLine = reader.ReadLine();
            while (readLine != null) {
                IList<IToken> tokenizeLine = TokenizeLine(readLine);
                tokens.AddRange(tokenizeLine);
                readLine = reader.ReadLine();
            }
            return tokens;
        }
        private IList<IToken> TokenizeLine(string readLine) {
            IList<IToken> tokens = new List<IToken>();
            Match vsanInterfaceDeclartion = Regex.Match(readLine, "vsan ([0-9]+) interfaces:");
            if (vsanInterfaceDeclartion.Success) {
                Group numberGroup = vsanInterfaceDeclartion.Groups[1];
                VsanToken vsanToken = new VsanToken(numberGroup.Value);
                tokens.Add(vsanToken);
                return tokens;
            }
            Match vsanInterface = Regex.Match(readLine, "(fc[0-9]/[0-9]+)");
            if (vsanInterface.Success) {
                GroupCollection groupCollection = vsanInterface.Groups;
                foreach (Group vsanInterfaceGroup in groupCollection) {
                    string value = vsanInterfaceGroup.Value;
                    IToken token = new InterfaceToken(value);
                    tokens.Add(token);
                }
            }
            return tokens;
        }
        public IList<IToken> Tokens {
            get {
                return tokens;
            }
        }
    }
    class VsanTokenInterpreter {
        private readonly IList<IToken> tokens;
        private readonly IList<IVSan> vSans;
        public VsanTokenInterpreter(IList<IToken> tokens) {
            this.tokens = tokens;
            this.vSans = ParseTokens(tokens);
        }
        private IList<IVSan> ParseTokens(IList<IToken> tokens) {
            IList<IVSan> vsans = new List<IVSan>();
            IVSan currentVSan = null;
            foreach (IToken token in tokens) {
                if (token is IVsanToken) {
                    currentVSan = CreateVSan((IVsanToken)token);
                    vsans.Add(currentVSan);
                } else if (token is IInterfaceToken) {
                    if (currentVSan == null)
                        throw new Exception("First Vsan Line has to be declared!");
                    IInterface inter = CreateInterface((IInterfaceToken)token);
                    currentVSan.Interfaces.Add(inter);
                }
            }
            return vsans;
        }
        protected virtual IInterface CreateInterface(IInterfaceToken interfaceToken) {
            //Edited: you can now access the First/Second Number from the Interface Token and use it to create the Instance of your interface:
            int firstNumber = interfaceToken.FirstNumber;
            int secondNumber = interfaceToken.SecondNumber;
            return new Interface(interfaceToken.Value);
        }
        protected virtual IVSan CreateVSan(IVsanToken vsanToken) {
            return new VSan(vsanToken.VsanInterfaceNumber, new List<IInterface>());
        }
        public IList<IVSan> VSans {
            get { return vSans; }
        }
        public IList<IToken> Tokens {
            get { return tokens; }
        }
    }

结果和结论:

结果,您得到一个包含IInterfaces的IVsan。您可以在代码中使用此模型来获取您要查找的信息。

  1. 通过使用对象模型,您可以随时更改解析,而不会改变您的业务逻辑
  2. 解析逻辑比Regex更简单易懂
  3. 可能存在可读异常(用于用户输入(
  4. 您可以在转换期间解析int/bools a.s.o
  5. 您的同事可以处理和维护您的代码

如果你有更多的问题,请问我,我很乐意帮助你!也请给我反馈,我愿意接受新的印象!

    List<string> interfaces = new List<string>();
    using (StringReader reader = new StringReader(subjectString))
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            Regex regexObj = new Regex(@"fc'd+/'d+");
            Match matchResults = regexObj.Match(line);
            while (matchResults.Success)
            {
                interfaces.Add(matchResults.Value);
                matchResults = matchResults.NextMatch();
            } 
        }
    }
    foreach (var inter in interfaces) Console.WriteLine("{0}", inter);

输出:

fc1/1
fc1/2
fc1/3
fc1/4
fc1/5
fc1/6
fc1/7
fc1/8
fc1/9
fc1/10
fc1/11
fc1/12
fc1/13
fc1/14
fc1/15
fc1/16