2009-09-11 2 views
14

Я пытаюсь создать общую комбинацию форматировщика/парсера.Форматирование строки форматирования

Пример сценария:

  • У меня есть строка для string.Format(), например, var format = "{0}-{1}"
  • У меня есть массив объектов (строка) для ввода, например. var arr = new[] { "asdf", "qwer" }
  • Я форматирую массив, используя строку формата, например. var res = string.Format(format, arr)

То, что я пытаюсь сделать, это вернуть обратно отформатированную строку обратно в массив объекта (строки). Что-то вроде (псевдо-код):

var arr2 = string.Unformat(format, res) 

// when: res = "asdf-qwer"  
// arr2 should be equal to arr 

У кого-нибудь есть что-то подобное? Я думаю об использовании регулярных выражений (изменить исходную строку формата, а затем передать ее в Regex.Matches для получения массива) и запустить ее для каждого заполнителя в строке формата. Возможно ли это или есть другое более эффективное решение?

+0

Как долго строка, которая неформатируется? –

+0

@ Крис: в разумных пределах. ATM, я использую это только для имен файлов. –

+0

Обратите внимание, что с учетом общего характера результаты могут быть неоднозначными - например, 'format = '{0} - {1}'' и 'arr = {" as-df "," qw-er "}'. Не может быть отформатирован тремя способами. Вам нужно определить, как справляться с неопределенностями, или ограничить содержание строки формата и значения. – peterchen

ответ

9

Вы не можете форматировать, поскольку информация теряется. String.Format - это «разрушительный» алгоритм, что означает, что вы не можете (всегда) вернуться назад.

Создайте новый класс, унаследованный от string, где вы добавляете элемент, который следит за "{0}-{1}" и { "asdf", "qwer" }, переопределить ToString() и изменить немного кода.

Если это становится слишком сложным, просто создайте тот же класс, но не наследуйте от string и немного измените свой код.

ИМО, это лучший способ сделать это.

+0

Немного больше работы, но очень возможно. –

2

Это просто невозможно в общем случае. Некоторая информация будет «потеряна» (границы строк) в методе Format. Предположите:

String.Format("{0}-{1}", "hello-world", "stack-overflow"); 

Как вы "неформатируете" это?

+0

Хорошая точка. Как насчет создания меньшего, чем общее решение, которое имеет предположение о отсутствии символа в формате, будет присутствовать в массиве объектов? –

+4

Adrian: В некоторых случаях это было бы неоднозначно: 'String.Format (" {0} {1} "," 12 "," 3 ")' вернет «123», но вы не можете сделать вывод из строки формата что это «12», «3» или «12», «3» или ... –

+0

. Вы вернете массив результатов и позвольте клиенту справиться с этим. – toddmo

2

Предполагая, что «-» нет в исходных строках, можете ли вы не просто использовать Split?

var arr2 = formattedString.Split('-'); 

Обратите внимание, что это относится только к представленному примеру с допущением. Любой обратный алгоритм зависит от вида используемого форматирования; возможно, даже невозможна обратная операция, о чем свидетельствуют другие ответы.

+0

Формат может быть любым. Но да, мы должны согласиться с тем, что что-либо в формате не должно отображаться в отформатированном массиве. –

+0

Добавлены некоторые пояснения к ответу. –

1

Простое решение может быть

  • заменить все маркеры формата с (. *)
  • побег все другие специальные Charaters в format
  • сделать матч регулярное выражение не жадный

Это позволит устранить двусмысленности в кратчайшие возможные совпадения.

(я не хорошо RegEx, поэтому, пожалуйста, поправьте меня, ребята :))

0

После форматирования, вы можете поместить полученную строку и массив объектов в словарь со строкой в ​​качестве ключа:

Dictionary<string,string []> unFormatLookup = new Dictionary<string,string []> 
... 
var arr = new string [] {"asdf", "qwer" }; 
var res = string.Format(format, arr); 
unFormatLookup.Add(res,arr); 

и в методе неформат, вы можете просто передать строку и посмотреть эту строку и возвращает массив, используемый:

string [] Unformat(string res) 
{ 
    string [] arr; 
    unFormatLoopup.TryGetValue(res,out arr); //you can also check the return value of TryGetValue and throw an exception if the input string is not in. 
    return arr; 
} 
14

Хотя комментарии о потерянной информации действительны, когда-то s вы просто хотите получить строковые значения строки с известным форматированием.

Один метод: this blog post, написанный моим другом. Он реализовал метод расширения под названием string[] ParseExact(), родственный DateTime.ParseExact(). Данные возвращаются как массив строк, но если вы можете жить с этим, это очень удобно.

public static class StringExtensions 
{ 
    public static string[] ParseExact(
     this string data, 
     string format) 
    { 
     return ParseExact(data, format, false); 
    } 

    public static string[] ParseExact(
     this string data, 
     string format, 
     bool ignoreCase) 
    { 
     string[] values; 

     if (TryParseExact(data, format, out values, ignoreCase)) 
      return values; 
     else 
      throw new ArgumentException("Format not compatible with value."); 
    } 

    public static bool TryExtract(
     this string data, 
     string format, 
     out string[] values) 
    { 
     return TryParseExact(data, format, out values, false); 
    } 

    public static bool TryParseExact(
     this string data, 
     string format, 
     out string[] values, 
     bool ignoreCase) 
    { 
     int tokenCount = 0; 
     format = Regex.Escape(format).Replace("\\{", "{"); 

     for (tokenCount = 0; ; tokenCount++) 
     { 
      string token = string.Format("{{{0}}}", tokenCount); 
      if (!format.Contains(token)) break; 
      format = format.Replace(token, 
       string.Format("(?'group{0}'.*)", tokenCount)); 
     } 

     RegexOptions options = 
      ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None; 

     Match match = new Regex(format, options).Match(data); 

     if (tokenCount != (match.Groups.Count - 1)) 
     { 
      values = new string[] { }; 
      return false; 
     } 
     else 
     { 
      values = new string[tokenCount]; 
      for (int index = 0; index < tokenCount; index++) 
       values[index] = 
        match.Groups[string.Format("group{0}", index)].Value; 
      return true; 
     } 
    } 
} 
+0

Что возвращается в этой ситуации: '" a-b-c ".ParseExact (" {0} - {1} - {0} ")'? – Zarepheth

+0

Предложение - заменить 'format = format.Replace (token, string.Format (" (? 'Group {0}'. *) ", TokenCount));' с 'format = format.ReplaceFirst (токен, строка.Format ("(? 'group {0}'. *)", tokenCount)); format = format.Replace (токен, string.Format ("\\ {0}", tokenCount)); '. Это должно лучше обрабатывать строки формата, которые используют входные параметры несколько раз. ReplaceFirst Источник: http://stackoverflow.com/questions/141045/how-do-replace-the-first-instance-of-a-string-in-net#141076 – Zarepheth

+0

Не нравится "abc" .ParseExact ("{0} {1} {2}") и @ "a $ - \ & * b^c" .ParseExact (@ "{0} $ - \\ & * {1}^{ 2} ") – CRice

Смежные вопросы