3

我正在解析 C# 中的一些输入,并且我正在使用 RegEx 处理遇到问题。

免责声明:我不是正则表达式专家,但我正在学习更多。

我有一个如下所示的输入字符串:

ObjectType [property1=value1, property2=value2, property3=AnotherObjectType [property4=some value4]]

(一个人为的值,但重要的是这些可以嵌套)。

我正在执行以下操作来标记字符串:

Regex Tokenizer = new Regex(@"([=\[\]])|(,\s)");
string[] tokens = Tokenizer.Split(s);

这让我完成了大约 98% 的工作。这会将字符串拆分为已知的分隔符,逗号后跟一个空格。

上例中的标记是:

ObjectType
[
property1
=
value1
,   
property2
=
value2
,
property3
=
AnotherObjectType
[
property4
=
some value4
]
]

但我有两个问题:

1) 属性值可以包含逗号。这是一个有效的输入:

ObjectType [property1=This is a valid value, and should be combined,, property2=value2, property3=AnotherObjectType [property4=value4]]

我希望 property1= 之后的令牌是:

This is a valid value, and should be combined,

我希望保留令牌内的空格。目前,它在找到逗号时被拆分。

2) 拆分时,逗号标记包含空格。如果可能的话,我想摆脱这个,但这是一个不太重要的优先事项。

我尝试了各种选择,但它们都让我部分实现了目标。我所拥有的最接近的是:

    Regex Tokenizer = new Regex(@"([=\[\]])|(,\s)|([\w]*\s*(?=[=\[\]]))|(.[^=]*(?=,\s))");

为了匹配分隔符,逗号后跟空格,单词字符后跟文字前的空格,以及逗号和空格之前的文本(不包括 = 符号)。

当我得到匹配而不是调用 split 时,我得到了这个:

ObjectType
[
property1
=
value1
,   
property2
=
value2
,
property3
=
AnotherObjectType
[
property4
=
value4
]
]

请注意属性 4 中缺少的信息。更复杂的输入有时会在令牌中包含右括号,如下所示: value4] 我不确定为什么会发生这种情况。关于如何改进这一点的任何想法?

谢谢,菲尔

4

2 回答 2

0

使用词法分析器和解析器工具最容易回答这个问题。许多人认为它们对于这些“简单”的用例来说太复杂了,尽管我一直发现它们更清晰、更容易推理。你不会陷入愚蠢的逻辑。

对于 C#,GPLEXGPPG似乎是一些不错的选择。请参阅此处了解您可能想要使用它们的原因

在您的情况下,您有一个语法,这就是您如何根据上下文定义不同标记之间的交互。此外,您还拥有在您选择的语言和工具链中实现此语法的详细信息。语法相对容易定义,您已经非正式地这样做了。细节是棘手的部分。如果您有一个框架可以读取一些已定义的写出语法位的方式并生成代码来实际执行它,那不是很好吗?

简而言之,这就是这些工具的工作方式。文档很短,因此请通读所有文档,提前花点时间会很有帮助。

本质上,您将声明一个扫描器和解析器。扫描器接收文本流/文件并将其与各种正则表达式进行比较,直到匹配。该匹配作为令牌传递给解析器。然后下一个标记被匹配并向上传递,一轮又一轮,直到文本流清空。

每个匹配的标记都可以附加任意 C# 代码,并且与解析器中的每个规则相同。

我通常不使用 C#,但我已经编写了很多词法分析器和解析器。不同语言的原则是相同的。这是解决您的问题最佳解决方案,并将在您的整个职业生涯中一次又一次地帮助您。

于 2013-02-06T19:08:40.620 回答
0

您可以使用两个正则表达式和一个递归函数来做到这一点,但有一个警告:必须转义特殊字符。从我所见,"=""["具有特殊含义,因此如果您希望它们作为属性值的一部分出现,则"]"必须在这些字符之前插入 a 。"\"请注意,逗号不被视为“特殊”。字符串前的逗号"property="会被忽略,否则不会以特殊方式处理(事实上,在属性之间是可选的)。

输入

ObjectType
[
    property1=value1,val\=value2   
    property2=value2 \[property2\=this is not an object\], property3=
        AnotherObjectType [property4=some 
value4]]

常用表达

用于发现“复杂”类型的正则表达式(以类型名称开头,后跟方括号)。正则表达式包括一种平衡方括号的机制,以确保每个左括号都与一个右括号配对(这样匹配不会过早或过晚结束):

^\s*(?<TypeName>\w+)\s*\[(?<Properties>([^\[\]]|\\\[|\\\]|(?<!\\)\[(?<Depth>)|(?<!\\)\](?<-Depth>))*(?(Depth)(?!)))\]\s*$

用于发现复杂类型中的属性的正则表达式。请注意,这还包括平衡的方括号,以确保子复杂类型的属性不会被父级意外使用。

(?<PropertyName>\w+)\s*=\s*(?<PropertyValue>([^\[\]]|\\\[|\\\]|(?<!\\)\[(?<Depth>)|(?<!\\)\](?<-Depth>))*?(?(Depth)(?!))(?=$|(?<!\\)\]|,?\s*\w+\s*=))

代码

private static Regex ComplexTypeRegex = new Regex( @"^\s*(?<TypeName>\w+)\s*\[(?<Properties>([^\[\]]|\\\[|\\\]|(?<!\\)\[(?<Depth>)|(?<!\\)\](?<-Depth>))*(?(Depth)(?!)))\]\s*$" );
private static Regex PropertyRegex = new Regex( @"(?<PropertyName>\w+)\s*=\s*(?<PropertyValue>([^\[\]]|\\\[|\\\]|(?<!\\)\[(?<Depth>)|(?<!\\)\](?<-Depth>))*?(?(Depth)(?!))(?=$|(?<!\\)\]|,?\s*\w+\s*=))" );

private static string Input = 
    @"ObjectType" + "\n" +
    @"[" + "\n" +
    @"    property1=value1,val\=value2   " + "\n" +
    @"    property2=value2 \[property2\=this is not an object\], property3=" + "\n" +
    @"        AnotherObjectType [property4=some " + "\n" + 
    @"value4]]";

static void Main( string[] args )
{
    Console.Write( Process( 0, Input ) );
    Console.WriteLine( "\n\nPress any key..." );
    Console.ReadKey( true );
}

private static string Process( int level, string input )
{
    var l_complexMatch = ComplexTypeRegex.Match( input );

    var l_indent = string.Join( "", Enumerable.Range( 0, level * 3 ).Select( i => " " ).ToArray() );

    var l_output = new StringBuilder();

    l_output.AppendLine( l_indent + l_complexMatch.Groups["TypeName"].Value );

    foreach ( var l_match in PropertyRegex.Matches( l_complexMatch.Groups["Properties"].Value ).Cast<Match>() )
    {
        l_output.Append( l_indent + "@" + l_match.Groups["PropertyName"].Value + " = " );

        var l_value = l_match.Groups["PropertyValue"].Value;

        if ( Regex.IsMatch( l_value, @"(?<!\\)\[" ) )
        {
            l_output.AppendLine();
            l_output.Append( Process( level + 1, l_value ) );
        }
        else
        {
            l_output.AppendLine( "\"" + l_value + "\"" );
        }

    }

    return l_output.ToString();
}

输出

ObjectType
@property1 = "value1,val\=value2  "
@property2 = "value2 \[property2\=this is not an object\]"
@property3 = 
   AnotherObjectType
   @property4 = "some value4"

如果您无法逃脱分隔符,那么我怀疑即使是人类也可以解析这样的字符串。例如,人类如何可靠地知道属性 3 的值应该被视为文字字符串还是复杂类型?

于 2013-02-06T16:18:30.727 回答