【问题标题】:Is there an equivalent to 'sscanf()' in .NET?.NET 中是否有等效于“sscanf()”的功能?
【发布时间】:2009-01-29 16:08:31
【问题描述】:

.NET Framework 为我们提供了Format 方法:

string s = string.Format("This {0} very {1}.", "is", "funny");
// s is now: "This is very funny."

我想要一个“取消格式化”功能,例如:

object[] params = string.Unformat("This {0} very {1}.", "This is very funny.");
// params is now: ["is", "funny"]

我知道 ANSI-C 库中存在类似的东西(printfscanf)。

问题:C# 中有类似的东西吗?

更新:使用正则表达式捕获组不是我需要的解决方案。它们也是一种方式。我正在寻找一种可以双向以单一格式工作的系统。放弃一些功能(如类型和格式信息)是可以的。

【问题讨论】:

    标签: .net string


    【解决方案1】:

    没有这样的方法,可能是因为解决歧义的问题:

    string.Unformat("This {0} very {1}.", "This is very very funny.")
    // are the parameters equal to "is" and "very funny", or "is very" and "funny"?
    

    正则表达式capturinggroups是针对这个问题制作的;你可能想调查一下。

    【讨论】:

      【解决方案2】:

      带分组的正则表达式?

      /This (.*?) very (.*?)./
      

      【讨论】:

        【解决方案3】:

        如果有人感兴趣,我刚刚发布了一个 scanf() 替换 .NET。如果正则表达式不适合您,我的代码非常接近 scanf() 格式字符串。

        你可以在http://www.blackbeltcoder.com/Articles/strings/a-sscanf-replacement-for-net看到和下载我写的代码。

        【讨论】:

        • 您的课程似乎不适用于浮点数。输入 = "von 95.50 bis 120.00 EUR" 格式为 "von %f bis %f %s" 应返回 {95.50, 120.00, "EUR"} 但返回 {9550, 12000, "EUR"}。
        • 好的,我发现了问题。您对 double.TryParse 的调用应该是: double.TryParse(input.Extract(start, input.Position), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture.NumberFormat, out result) 否则它只会起作用对于在语言环境设置中使用点作为小数分隔符的人。
        • 谢谢,我去看看。很抱歉错过了您之前的评论。
        • @JonathanWood - 我正在使用您的 ScanFormatted 课程,但是您想如何准确地读取带有前导零的数字?我的字符串是“20 098 20 46 22.000”我使用格式字符串“%2d %3d %2d %2d %f”但它返回“20,0,98,20,46”而不是“20,98,20 ,46,22"。
        • @SecurityHound:我已经有 10 年没看到这个问题了,但似乎问题可能是因为它将一个以 0 开头的数字解释为八进制(它将以 '8' 或'9' 位)。您可以拉动该部分以使其正常工作。只需将ParseDecimal() 中的第一个else 子句注释掉即可。
        【解决方案4】:

        你可以做string[] parts = string.Split(' '),然后在你的例子中通过索引位置parts[1]和parts [3]提取。

        【讨论】:

        • 这有效,但仅适用于此字符串,因为 {} 值中没有空格。否则索引将关闭。
        【解决方案5】:

        是的。这些被称为“正则表达式”。会做这件事的人是

        This (?<M0>.+) very (?<M1>.+)\.
        

        【讨论】:

        • 我认为关键是要规避 RegEx 过于复杂和神秘的语法,并在不需要复杂性的一般情况下提供轻量级和简单的东西。因此,对于您想要进行模式匹配的一般情况,string.Format 的格式是非常可取的并且是自描述的。
        【解决方案6】:

        @mquander:实际上,PHP 解决它的方式甚至不同:

        $s = "This is very very funny.";
        $fmt = "This %s very %s.";
        sscanf($s, $fmt, $one, $two);
        echo "<div>one: [$one], two: [$two]</div>\n";
        //echo's: "one: [is], two: [very]"
        

        但也许你的正则表达式评论可以帮助我。我只需要将"This {0} very {1}." 重写为:new Regex(@"^This (.*) very (.*)\.$")。这应该以编程方式完成,因此我可以在公共类接口上使用一种格式字符串。

        顺便说一句:我已经有一个解析器来查找参数:请参阅 Phil HaackNamed Format Redux 博客条目(是的,我还希望命名参数能够双向工作)。

        【讨论】:

        • 在本例中,PHP 的行为方式与标准 C sscanf 相同。 Sscanf 不会将空格读入%s 变量。
        【解决方案7】:

        我遇到了同样的问题,我相信有一个使用 REGEX 的优雅解决方案......但是在 C# 中提出了一个“UnFormat”函数,效果很好。很抱歉缺少 cmets。

            /// <summary>
            /// Unformats a string using the original formating string. 
            /// 
            /// Tested Situations:
            ///    UnFormat("<nobr alt=\"1\">1<nobr>", "<nobr alt=\"{0}\">{0}<nobr>") : "1"
            ///    UnFormat("<b>2</b>", "<b>{0}</b>") : "2"
            ///    UnFormat("3<br/>", "{0}<br/>") : "3"
            ///    UnFormat("<br/>4", "<br/>{0}") : "4"
            ///    UnFormat("5", "") : "5"
            ///    UnFormat("<nobr>6<nobr>", "<nobr>{0}<nobr>") : "6"
            ///    UnFormat("<nobr>2009-10-02<nobr>", "<nobr>{0:yyyy-MM-dd}<nobr>") : "2009-10-02"
            ///    UnFormat("<nobr><nobr>", "<nobr>{0}<nobr>") : ""
            ///    UnFormat("bla", "<nobr>{0}<nobr>") : "bla"
            /// </summary>
            /// <param name="original"></param>
            /// <param name="formatString"></param>
            /// <returns>If an "unformat" is not possible the original string is returned.</returns>
            private Dictionary<int,string> UnFormat(string original, string formatString)
            {
               Dictionary<int, string> returnList = new Dictionary<int, string>();
        
               try{
                  int index = -1;
        
                  // Decomposes Format String
                  List<string> formatDecomposed = new List<string> (formatString.Split('{'));
                  for(int i = formatDecomposed.Count - 1; i >= 0; i--)
                  {
                     index = formatDecomposed[i].IndexOf('}') + 1;
        
                     if (index > 0 && (formatDecomposed[i].Length - index) > 0)
                     {
                        formatDecomposed.Insert(i + 1, formatDecomposed[i].Substring(index, formatDecomposed[i].Length - index));
                        formatDecomposed[i] = formatDecomposed[i].Substring(0, index);
                     }
                     else
                        //Finished
                        break;
                  }
        
                  // Finds and indexes format parameters
                  index = 0;
                  for (int i = 0; i < formatDecomposed.Count; i++)
                  {
                     if (formatDecomposed[i].IndexOf('}') < 0)
                     {
                        index += formatDecomposed[i].Length;
                     }
                     else
                     {
                        // Parameter Index
                        int parameterIndex;
                        if (formatDecomposed[i].IndexOf(':')< 0)
                           parameterIndex = Convert.ToInt16(formatDecomposed[i].Substring(0, formatDecomposed[i].IndexOf('}')));
                        else
                           parameterIndex = Convert.ToInt16(formatDecomposed[i].Substring(0, formatDecomposed[i].IndexOf(':')));
        
                        // Parameter Value
                        if (returnList.ContainsKey(parameterIndex) == false)
                        {
                           string parameterValue;
        
                           if (formatDecomposed.Count > i + 1)
                              if (original.Length > index)
                                 parameterValue = original.Substring(index, original.IndexOf(formatDecomposed[i + 1], index) - index);
                              else
                                 // Original String not valid
                                 break;
                        else
                           parameterValue = original.Substring(index, original.Length - index);
        
                        returnList.Add(parameterIndex, parameterValue);
                        index += parameterValue.Length;
                     }
                     else
                        index += returnList[parameterIndex].Length;
        
                     }
                  }
        
                  // Fail Safe #1
                  if (returnList.Count == 0) returnList.Add(0, original);
               } 
               catch
               {
                  // Fail Safe #2
                  returnList = new Dictionary<int, string>();
                  returnList.Add(0, original);
               }
        
               return returnList;
            }
        

        【讨论】:

          【解决方案8】:

          我参考了之前的回复,写了一个样例见下文

          string sampleinput = "FirstWord.22222";
          
          Match match = Regex.Match(sampleinput, @"(\w+)\.(\d+)$", RegexOptions.IgnoreCase);
          
          if(match.Success){
          
              string totalmatchstring = match.Groups[0]; // FirstWord.22222
              string firstpart = match.Groups[1]; // FirstWord`
              string secondpart = match.Groups[2]; // 22222
          
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-28
            • 2011-02-13
            • 2014-08-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多