需要一些正则表达式帮助

时间:2011-11-20 15:28:59

标签: c# regex

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

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正确分组,但我只得到每个输出中的第一个接口(fcnn / nn)。例如,在vsan 10中我想要所有接口,但我只获得fc1 / 1 这是我正在使用的正则表达式:

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

3 个答案:

答案 0 :(得分:1)

根据 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);

您只获得第一个界面,因为您的正则表达式请求匹配(简化):

vsan X interfaces:
fcX/X

这意味着您希望vsan X interfaces:出现在每个fcX/X的前面,而在您的字符串中则不是这样。

答案 1 :(得分:0)

    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

答案 2 :(得分:0)

它最好的解决方案是Tokenizer / Parser来获取您想要的信息。 我认为你不能用一个正则表达式处理它(对其他人有效和透明)。在我看来,正则表达式并不总是复杂字符串问题的解决方案。

我也认为,大型正则表达式在任何时候都是不可维护的,因此应该避免。 (自己尝试一下,从任何来自互联网的页面获取任何复杂的正则表达式。不要阅读,表达式应该做什么,并尝试描述表达式的功能。你会被赋予作者所期望的,以及你所期望的要做的表达。

简单的正则表达式强调我的观点:

这个正则表达式做了什么?

(?:[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])+)\])

此正则表达式是this页面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创建,而不是由正则表达式与组匹配。请参阅方法TokenizeTokenizeLine。他们做了很多努力。

  1. 分裂(线和空格)
  2. 匹配(如果Vsan或接口)

  3. 拆分后,它变得非常简单。只需向前移动代币并创建VSanInterface对象 令牌是按字符串组的顺序排序(由令牌中的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. 可以使用可读的异常(对于用户输入)
    5. 你可以解决int / bools a.s.o.转型期间
    6. 您的同事可以处理和维护您的代码。
    7. 如果您有疑问,请问我,我很乐意帮助您!还请给我反馈,我愿意接受新的印象!