需要一些正则表达式帮助
本文关键字:正则表达式 帮助 | 更新日期: 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创建的。参见方法Tokenize
和TokenizeLine
。他们做艰苦的工作。
- 拆分(直线和空格(
- 匹配(如果是Vsan或Interface(
拆分后,它变得非常简单。只需在Tokens中前进并创建VSan
和Interface
对象
令牌的顺序在字符串组(由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。您可以在代码中使用此模型来获取您要查找的信息。
- 通过使用对象模型,您可以随时更改解析,而不会改变您的业务逻辑
- 解析逻辑比Regex更简单易懂
- 可能存在可读异常(用于用户输入(
- 您可以在转换期间解析int/bools a.s.o
- 您的同事可以处理和维护您的代码
如果你有更多的问题,请问我,我很乐意帮助你!也请给我反馈,我愿意接受新的印象!
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