【问题标题】:C# Finding specific values from messageformat stringsC# 从消息格式字符串中查找特定值
【发布时间】:2019-10-15 07:50:16
【问题描述】:

给定一个消息格式字符串,例如下面的str。我希望能够获取用于显示文本值的“通知”和“名称”值。

var str = @"You have {notifications, plural,
          zero {no notifications}
           one {one notification}
           =42 {a universal amount of notifications}
         other {# notifications}
        }. Have a nice day, {name}!";

我尝试过使用regex,例如:

var matches = Regex.Matches(str, @"{(.*?)}");
//var matches = Regex.Matches(str, @"(?<=\{)[^}{]*(?=\})");
var results = matches.Cast<Match>().Select(m => m.Groups[1].Value).Distinct().ToList();

但是上面没有考虑到{notifications,.. 本身是用大括号括起来的,并且包括不需要的内部值,这些值也用大括号括起来。

简而言之,我只想能够解析一个字符串,例如上面的str,并在返回值处获得notificationsname

var str2 = @"Hello {name}" 这样的字符串应该只返回name 作为值。

编辑

notificationsname 的值不会提前知道 - 我只是以此为例,对于我需要从字符串返回的值。

【问题讨论】:

    标签: c# regex string-matching messageformat


    【解决方案1】:

    这样做的一种方法是编写一个方法,该方法将根据输入 count 和字符串的单数(和复数)形式为您格式化字符串:

    private static string FormatWord(int count, string singluar)
    {
        return Format(count, singluar, singluar + "s");
    }
    
    private static string FormatWord(int count, string singular, string plural)
    {
        return count == 0 ? "no " + plural
            : count == 1 ? "one " + singular
            : count == 42 ? "a universal number of " + plural
            : count + " " + plural;
    }
    

    然后在使用它可能看起来像:

    private static void Main()
    {
        var name = "User";
    
        while (true)
        {
            var count = GetIntFromUser("Enter notification count: ");
            Console.WriteLine($"You have {FormatWord(count, "notification")}. " + 
                $"Have a nice day, {name}");
        }
    }
    

    请注意,此方法还使用辅助方法从用户那里获取强类型整数:

    private static int GetIntFromUser(string prompt, Func<int, bool> validator = null)
    {
        int result;
        var cursorTop = Console.CursorTop;
    
        do
        {
            ClearSpecificLineAndWrite(cursorTop, prompt);
        } while (!int.TryParse(Console.ReadLine(), out result) ||
                 !(validator?.Invoke(result) ?? true));
    
        return result;
    }
    
    private static void ClearSpecificLineAndWrite(int cursorTop, string message)
    {
        Console.SetCursorPosition(0, cursorTop);
        Console.Write(new string(' ', Console.WindowWidth));
        Console.SetCursorPosition(0, cursorTop);
        Console.Write(message);
    }
    

    【讨论】:

      【解决方案2】:

      TL;DR:这是一个可选的解决方案

      var str = @"You have {notifications, plural,
                zero {no notifications}
                 one {one notification}
                 =42 {a universal amount of notifications}
               other {# notifications}
              }. Have a nice day, {name}!";
      
      // get matches skipping nested curly braces
      var matches = 
          Regex.Matches(str, @"{((?:[^{}]|(?<counter>{)|(?<-counter>}))+(?(counter)(?!)))}");
      
      var results = matches.Cast<Match>().Select(m => m.Groups[1].Value).Distinct()
          .Select(v => Regex.Match(v, @"^\w+").Value) // take 1st word
          .ToList();
      

      导致(在调试时从 Visual Studio Locals 窗口复制)

      results Count = 2   System.Collections.Generic.List<string>
          [0] "notifications"
          [1] "name"
      

      ...原始答案如下...


      关于原问题中的当前解决方案需要注意的一点:

      • . 的使用与换行符不匹配,因此这是它当前匹配嵌套值的原因之一(请参阅此source

      如果我理解你的目标,这篇文章是对相关问题和解决方案的一个很好的解释和演示:

      (本文解决了原始问题中提到的主要挑战——嵌套花括号

      https://blogs.msdn.microsoft.com/timart/2013/05/14/nestedrecursive-regex-and-net-balancing-groups-detect-a-function-with-a-regex/

      从那篇文章中,我建议将以下模式作为可选解决方案:

      var str = @"You have {notifications, plural,
                zero {no notifications}
                 one {one notification}
                 =42 {a universal amount of notifications}
               other {# notifications}
              }. Have a nice day, {name}!";
      
      // get matches skipping nested curly braces
      var matches = 
          Regex.Matches(str, @"{((?:[^{}]|(?<counter>{)|(?<-counter>}))+(?(counter)(?!)))}");
      var results = matches.Cast<Match>().Select(m => m.Groups[1].Value).Distinct().ToList();
      

      导致(在调试时从 Visual Studio Locals 窗口复制)

      results Count = 2   System.Collections.Generic.List<string>
          [0] "notifications, plural,\r\n          zero {no notifications}\r\n           one {one notification}\r\n           =42 {a universal amount of notifications}\r\n         other {# notifications}\r\n        "
          [1] "name"
      

      (或者如果您要将这些结果打印到控制台):

      // Result 0 would look like:
      notifications, plural,
                zero {no notifications}
                 one {one notification}
                 =42 {a universal amount of notifications}
               other {# notifications}
      
      
      // Result 1 would look like:
      name
      

      更新

      我回过头来意识到这个问题只要求单个单词作为结果。

      然后从每个结果中取出第一个单词

      (我用附加的 select 语句重复上面的 sn-p 以显示完整的解决方案)

      var str = @"You have {notifications, plural,
                zero {no notifications}
                 one {one notification}
                 =42 {a universal amount of notifications}
               other {# notifications}
              }. Have a nice day, {name}!";
      
      // get matches skipping nested curly braces
      var matches = 
          Regex.Matches(str, @"{((?:[^{}]|(?<counter>{)|(?<-counter>}))+(?(counter)(?!)))}");
      
      var results = matches.Cast<Match>().Select(m => m.Groups[1].Value).Distinct()
          .Select(v => Regex.Match(v, @"^\w+").Value) // take 1st word
          .ToList();
      

      导致(在调试时从 Visual Studio Locals 窗口复制)

      results Count = 2   System.Collections.Generic.List<string>
          [0] "notifications"
          [1] "name"
      

      更多信息

      (我只是觉得这很有趣,并花了更多时间研究/学习,并认为值得包含更多相关信息)

      herehere 的对话中包含一些支持和反对使用正则表达式来解决此类问题的意见。

      • 我认为阅读这些意见并获得更全面的观点很有趣

      不管上述观点如何,.NET 创建者认为实现平衡组定义是合适的——这个答案使用的功能:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多