Существует ли эквивалент «sscanf ()» в .NET?

#c# #razor

Вопрос:

Платформа .NET Framework предоставляет нам метод форматирования:

 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 (printf vs scanf).

Вопрос: есть ли что-то подобное в C#?

Обновление: Захват групп с помощью регулярных выражений-это не то решение, которое мне нужно. Они также являются одним из способов. Я ищу систему, которая может работать в обоих направлениях в одном формате. Можно отказаться от некоторых функций (например, от информации о типах и форматировании).

Ответ №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"?
 

Для этой проблемы созданы группы захвата регулярных выражений; возможно, вам захочется изучить их.

Ответ №2:

Регулярное выражение с группировкой?

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

Ответ №3:

Если кому-то интересно, я только что опубликовал scanf() замену для .NET. Если регулярные выражения не совсем подходят для вас, мой код scanf() довольно точно следует строке формата.

Вы можете посмотреть и скачать код, который я написал по адресу http://www.blackbeltcoder.com/Articles/strings/a-sscanf-replacement-for-net.

Комментарии:

1. Ваш класс, похоже, не работает с числами с плавающей запятой. Ввод = «фон 95,50 бис 120,00 ЕВРО» с форматом «фон %f бис %f %s» должен возвращать {95,50, 120,00, «ЕВРО»}, но возвращает {9550, 12000, «ЕВРО»}.

2. Хорошо, я нашел проблему. Твой призыв удвоиться. Попытка анализа должна быть: двойной. Попробуйте проанализировать(ввод. Извлечение(начало, ввод. Должность), Система. Глобализация. Количество стилей. Поплавок, Система. Глобализация. Информация о культуре. InvariantCulture.NumberFormat, out результат) в противном случае он будет работать только для людей, использующих точку в качестве десятичного разделителя в своих настройках локали.

3. Спасибо, я посмотрю на это. Извините, что пропустил ваш предыдущий комментарий.

4. @JonathanWood — Я использую ваш отформатированный класс, но как именно вы предполагаете прочитать число с начальным нулем? Моя строка «20 098 20 46 22.000» Я использую строку формата «- = — — %f», но она возвращает «20,0,98,20,46» вместо «20,98,20,46,22».

5. @SecurityHound: Прошло 10 лет с тех пор, как я смотрел на это, но, похоже, проблема может заключаться в том, что оно интерпретирует число, начинающееся с 0, как восьмеричное (которое заканчивается цифрой » 8 » или «9»). Вы могли бы вытащить эту часть, чтобы заставить ее работать. Просто прокомментируйте первое else предложение в ParseDecimal() .

Ответ №4:

Вы могли бы сделать string[] parts = строка.Разделите(‘ ‘), а затем извлеките по позиции индекса части[1] и части [3] в вашем примере.

Комментарии:

1. Это работает, но только для этой строки, так как в значениях {} нет пробелов. В противном случае индексы были бы отключены.

Ответ №5:

Ага. Они называются «регулярными выражениями». Тот, кто сделает это, — это

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

Комментарии:

1. Я думаю, что весь смысл в том, чтобы обойти слишком сложный и загадочный синтаксис регулярного выражения и предоставить что-то легкое и простое в общих случаях, когда вам не нужна сложность. Таким образом, формат строки. Формат очень желателен и самоописателен для общих случаев, когда вы хотите, чтобы шаблон соответствовал.

Ответ №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 (.*).$") . Это должно быть сделано программно, чтобы я мог использовать одну строку формата в интерфейсе общедоступного класса.

Кстати: У меня уже есть анализатор для поиска параметров: см. Запись в блоге Фила Хаака в названном формате Redux (и да, я также хочу, чтобы именованные параметры работали в обоих направлениях).

Комментарии:

1. В этом примере PHP ведет себя так же, как стандартный C sscanf. Sscanf не считывает пробелы в %s переменную.

Ответ №7:

Я столкнулся с той же проблемой, я верю, что существует элегантное решение с использованием регулярных выражений… но а придумал функцию в C# для «неформата», которая работает довольно хорошо. Извините за отсутствие комментариев.

     /// <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 amp;amp; (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

}