【问题标题】:How to parse a Ternary Statement in C#如何在 C# 中解析三元语句
【发布时间】:2017-06-08 11:58:43
【问题描述】:

我正在接受一个输入字符串,我希望它是一个适用于字符串的三元语句。所以我的方法签名看起来像这样:

public string Parse(string value, string ternaryStatement)  

并且有参数会给出这些结果:

Parse(null, "=?It's Null:It's Not Null") == "It's Null" // Empty string would 
Parse("", "=?It's Null:It's Not Null") == "It's Null"   // be same as null

这个例子比较简单,先用'?'分割字符串然后通过':'

但我当然需要一种方法来处理转义字符,“\”、“\?”和“:”,其中“\”在任何地方都有效,“\?”只会在第一个未转义的“?”之前有效并且“:”仅在相同的“?”之后才有效。

Parse(@"\?\", @"=\\\?\\?\:Match\::\:No Match\:") == ":Match:"
Parse(@"\?\", @"!=\\\?\\?\:No Match\::\:Match\:") == ":Match:"

但这真的很复杂。我相信我可以使用正则表达式来执行它,但这只会产生另一个问题,因为这远远超出了我对正则表达式的有限理解。解决这个问题的最佳方法是什么?

编辑 1

一些背景知识:我将 URL 的格式存储在数据库配置表中(实际上是 Dynamics 365 for Customer Engagement,但此时无关紧要)。格式存储为字符串,所需的参数在代码中定义。所以一般情况下是这样的:

格式:“https://something.com?Foo={0}&Bar={1}”

描述:“0 - Foo, 1 - Bar”

描述用于格式化 url 的人和需要知道如何构造格式语句的开发人员。

我现在遇到的问题是我的 url 至少需要两个不同参数之一。如果其中一个值为 null 或为空,则如果包含在 url 中,则会出错。所以我需要一种说法,如果 Foo 为 null 或 Bar 为 null,则不要包含名称或 &。理想情况下,我想这样实现:

"https://something.com?{0:=?:Foo={{0}}}&}{1:=?:Bar={{1}}}}" 

所以如果 Foo 为 null 而 Bar 为 "Bar" 则输出为

"https://something.com?Bar=Bar"

如果我们需要在布尔值的 0/1 和 true/false 之间切换而无需更改代码,我也可以看到它正在使用:

"https://something.com?{0:=0?false:true}" 

【问题讨论】:

  • 为什么要这样做?给我们一些背景信息,说明为什么这对您有用。
  • @mjwills 我正在尝试创建一个字符串格式化程序来处理空值等...所以Format("{0:=?My Default Value:{{0}}}", value) 我不能将该逻辑作为格式调用的参数,因为我需要它由字符串本身定义,我没有方法可以包含另一个默认字符串作为参数。
  • 您能否分享更多示例输入和输出?我仍在努力理解根本问题。我很抱歉。
  • @mjwills 更新有帮助吗?
  • 鉴于您的更新,也许最好看看 UriBuilder 吗? stackoverflow.com/a/14517976/361842

标签: c# regex parsing


【解决方案1】:

这两个正则表达式应该是:

Regex rx = new Regex(@"(?<=(?:^|[^\\])(?:\\\\)*)\?");
Regex rx2 = new Regex(@"(?<=(?:^|[^\\])(?:\\\\)*):");

像这样使用它们:

var m = rx.Match(str);

if (m.Success)
{
    int ix = m.Index;
}

两个rx的重点是搜索到的字符串(\?:)前面必须有

(?<=(?:^|[^\\])(?:\\\\)*)

那是字符串^ 的开头,或者不是\ ([^\\]) 加上零或偶数个\\(?:\\\\)*

一个多合一的正则表达式是:

Regex rx = new Regex(@"^(?<operator>=|!=|<=|>=|<|>)(?<cmp>(?:(?:\\.)|[^?:])*)\?(?<true>(?:(?:\\.)|[^?:])*):(?<false>(?:(?:\\.)|[^?:])*)$");

if (m.Success)
{
    string op = m.Groups["operator"].Value;
    string cmp = m.Groups["cmp"].Value;
    string true1 = m.Groups["true"].Value;
    string false1 = m.Groups["false"].Value;
}

op 中,您将获得使用的比较运算符,在cmp 中,比较符,在true1false1truefalse 字符串。如果!m.Success 则字符串格式不正确。理解正则表达式留给读者作为一个简单的练习(除非你理解一个正则表达式,否则你永远不应该使用它,因为在此之前或之后你必须修改它/修复它/调试它)

【讨论】:

  • 你的意思是我不能使用它,并在代码中放置一个指向这个答案的链接,以便我以后可以 ping 你? ;)
  • @Daryl 最后你可以为所欲为我住:-)
  • unless you comprehend a regex, you shouldn't ever use it ... 我的重构会很痛苦。
【解决方案2】:

根据输入字符串返回不同值的解决方案

为什么需要传入三元语句/这不是更有意义吗?

string Parse(string value, string returnIfNull, string returnIfNotNull) 
{
    return string.IsNullOrEmpty(value) ? returnIfNull: returnIfNotNull;
}

Console.WriteLine(Parse("", "treat as null", "not expected"));
Console.WriteLine(Parse("hello", "not expected", "this value's not null"));

解析值的三元字符串

但是,如果你真的需要这样做,你可以使用类似下面的东西:

private static readonly Regex TernaryParserRegex = new Regex(
    @"^=\?(?<ifNull>(?:\\(\\\\)*:|[^:])*)(?<!\\):(?<ifNotNull>(?:\\(\\\\)*:|[^:])*)$"
    /* , RegexOptions.Compiled //include this line for performance if appropriate */
);      
private string[] ParseTernaryString (string ternaryStatement)
{
    var results = TernaryParserRegex.Match(ternaryStatement);
    if (results.Success) 
    {
        string[] returnVal = {
            results.Groups["ifNull"].Value
            , 
            results.Groups["ifNotNull"].Value
        };
        return returnVal;
    }
    else
    {
        throw new Exception("Invalid Ternary Statement"); //use an appropriate exception type here; or have the function return `{null,null}` / some other default as appropriate 
    }
}
public string Parse(string value, string ternaryStatement) 
{
    var returnValues = ParseTernaryString(ternaryStatement);
    return string.IsNullOrEmpty(value) ? returnValues[0]: returnValues[1];
}

//Example Usage:
Console.WriteLine(Parse("", "=?treat as null:not expected"));
Console.WriteLine(Parse("hello", "=?not expected:this value's not null"));

此处提供了正则表达式的说明和其他示例: https://regex101.com/r/YJ9qd3/1


将非 null/空白值附加到 URL 的查询字符串

public void Main()
{
    var url = "https://example.com?something=keepMe&foo=FooWasHere&bar=swapMeQuick";
    var dict = new System.Collections.Generic.Dictionary<string, string>();
    dict.Add("foo", null);
    dict.Add("bar", "hello");
    dict.Add("boo", "new");
    Console.WriteLine(CreateUri(url, dict).ToString());
}

Uri CreateUri(string uri, IDictionary<string, string> parameters) 
{
    return CreateUri(new Uri(uri), parameters);
}
Uri CreateUri(Uri uri, IDictionary<string, string> parameters)
{
    var query = HttpUtility.ParseQueryString(uri.Query); //https://msdn.microsoft.com/en-us/library/ms150046(v=vs.110).aspx; though returns HttpValueCollection
    foreach (string key in parameters.Keys)
    {
        if (string.IsNullOrEmpty(parameters[key]))
        { //parameter is null or empty; if such a parameter already exists on our URL, remove it
            query.Remove(key); //if this parameter does not already exist, has no effect (but no exception is thrown)
        }
        else
        { //parameter has a value; add or update the query string with this value
            query.Set(key, parameters[key]);
        }
    }
    var builder = new UriBuilder(uri);
    builder.Query = query.ToString();
    return builder.Uri;
}

【讨论】:

  • 存储这些设置的表并不总是存储 url,所以虽然我看到这个工作,但它并没有完成我需要的。不过还是谢谢。
猜你喜欢
  • 2019-03-09
  • 2013-07-16
  • 2011-04-24
  • 2010-12-22
  • 1970-01-01
  • 2019-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多