在 C# 中的两个特殊字符之间拆分分层字符串

Posted

技术标签:

【中文标题】在 C# 中的两个特殊字符之间拆分分层字符串【英文标题】:Split hierarchical string between two special characters in C# 【发布时间】:2021-07-08 21:10:19 【问题描述】:

我正在使用类似的字符串

$upper('anothervalue')$

我写了一个解析器,用下面的代码很好地解析了这个语句。

但是,现在另一个系统正在发送一个复杂的字符串,例如(我可以得到任意数量的链式字符串):

$upper($trim('somevalue')$)$ - $upper('anothervalue')$

如何以特定顺序遍历分层数据以评估函数(从最里面的匹配开始):

$trim('somevalue')$ --我会评估这个,把它存储在变量x

$upper(x)$ --now 用上一条语句的结果计算上层

$upper('anothervalue')$

private static object EvaluateFunctionsInJson(string jsonValue)

    object origJsonValue = jsonValue;
    var fnMatches = Regex.Matches(jsonValue.ToString(), @"\$(.+?)\$");
    var fnCount = fnMatches.Count;
    foreach (var fnMatch in fnMatches)
    
        // call another method to evaluate the function
        object replaceValue = EvaluateFunction(fnMatch.ToString());

        if (fnCount > 1)
        
            origJsonValue = origJsonValue.ToString().Replace(fnMatch.ToString(), replaceValue.ToString());
        
        else
        
            origJsonValue = replaceValue;
        
    
    return origJsonValue;




private static object EvaluateFunction(string jsonValue)

    var functionWithoutDollarSign = Regex.Replace(jsonValue.ToString(), @"[$$]+", "");
    string functionName = Regex.Match(functionWithoutDollarSign, @"\b[A-Za-z]+\b", RegexOptions.Singleline).Value; //get the first word
    var functionParam = Regex.Match(functionWithoutDollarSign, @"\(([^)]*)\)").Value; //get the text between paranthesis
    var functionParamWithoutParanthesis = Regex.Replace(functionParam.ToString(), @"[\(\)]+", "");

    var funcParams = functionParamWithoutParanthesis.Split(',');
    var value = funcParams[0];
    switch (functionName.ToLower().Trim())
    
        case "upper":
            return value.ToUpper();
        case "lower":
            return value.ToLower();
        case "number":
            return Convert.ToInt64(value);
        case "boolean":
            return Convert.ToBoolean(value);
        default:
            return value;
    

【问题讨论】:

这需要有多复杂?您可能可以迭代地执行此操作,但它会很痛苦并且无法很好地扩展。您可以尝试使用递归做一些事情,但我认为在这种情况下使用表达式树会更好。 docs.microsoft.com/en-us/dotnet/csharp/programming-guide/… 我没想到它会如此复杂。只有两个层次——功能下的功能。我以前从未使用过表达式树,需要探索它 - 感谢您的链接。 这可能吗? “开始美元”和“结束美元”符号之间没有区别,那么您应该如何区分以表达式开头的美元符号和以表达式结尾的美元符号呢?就像,上面你更复杂的表达是“打开,打开,关闭,关闭,打开,关闭”,但你怎么知道它不是“打开,关闭,打开,关闭,打开,关闭”? 进一步思考:如果每​​个“开盘美元”后面都有一个已知关键字(“upper”、“trim”等),我想你可以先替换所有 那些 带有开始标记和关键字的实例,然后将所有剩余的美元符号替换为结束标记,然后您就会知道打开和关闭... 我不明白结束美元符号的必要性。为什么不干脆做类似$upper($trim('somevalue')) - $upper('anothervalue') 之类的事情,其中​​美元符号表示“函数”的开始,然后美元符号之后是函数名称,函数的参数用括号括起来。 【参考方案1】:

你可以使用

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

public class Test

    public static void Main()
    
        var text = "$upper($trim('somevalue   ')$)$ - $upper('anothervalue')$";
        var pattern = @"(?s)\$(?<functionName>\w+)\('?(?<value>[^'\\]*(?:\\.[^'\\]*)*)'?\)\$";
        var prev = string.Empty;
        do 
            prev = text;
            text = Regex.Replace(text, pattern, x => 
                switch (x.Groups["functionName"].Value.ToLower().Trim())
                
                    case "trim":
                        return x.Groups["value"].Value.Trim();
                    case "upper":
                        return x.Groups["value"].Value.ToUpper();
                    case "lower":
                        return x.Groups["value"].Value.ToLower();
                    case "number":
                        return Convert.ToInt64(x.Groups["value"].Value).ToString();
                    case "boolean":
                        return Convert.ToBoolean(x.Groups["value"].Value).ToString();
                    default:
                        return x.Groups["value"].Value;
                
            );
         while (prev != text);
        Console.WriteLine(text);
    

请参阅online C# demo。

正则表达式是

(?s)\$(?<functionName>\w+)\('?(?<value>[^'\\]*(?:\\.[^'\\]*)*)'?\)\$

请参阅online regex demo。 详情::

(?s) - 一个 RegexOptions.Singleline 内联正则表达式选项,使 . 匹配任何字符,包括换行符 \$ - 美元符号 (?&lt;functionName&gt;\w+) - 组“functionName”:一个或多个单词字符 \( - 一个 ( 字符 '? - 一个可选的 ' 字符 (?&lt;value&gt;[^'\\]*(?:\\.[^'\\]*)*) - 组“值”:除'\ 之外的零个或多个字符,然后出现零个或多个任何转义字符,然后是除'\ 之外的零个或多个字符 '?\) - ')) 字符串 \$

do ... while (prev != text) 搜索并替换 text 变量中的匹配项,直到找不到匹配项。

【讨论】:

这是一个复杂的正则表达式。但它适用于多种功能。老实说,我不擅长编辑正则表达式。我怎样才能使它适用于像$upper($append('somevalue', '-updated')$)$ 这样我处理多个参数的字符串。基本上,我猜是阅读() 中的任何内容。我的意思是,我可以为不同的功能设置不同的参数。 @SriReddy 将值捕获到“值”组捕获集合中,然后您将能够以列表的形式访问它们,请参阅this proof of concept。 概念证明实际上破坏了与您以前的正则表达式一起使用的功能,例如处理多个函数$trim($upper($lower('anothervalue')$)$)$。我想用这个逻辑使这些工作 - $trim($upper($lower('anothervalue')$)$)$$trim($upper($append('anothervalue', '--new')$)$)$$upper(mark)$$upper($ifempty('', 'unknown')$)$

以上是关于在 C# 中的两个特殊字符之间拆分分层字符串的主要内容,如果未能解决你的问题,请参考以下文章

拆分2列中的特殊字符并在oracle中合并为多行

正则表达式中的特殊字符

使用 C++ 中的特殊格式将字符串拆分为字符串

万用字元与特殊符号及正则表示字符

C#中的特殊字符打印[重复]

sql中如何判断字符串中含有特殊字符