Разбор форматированной строки - PullRequest
14 голосов
/ 11 сентября 2009

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

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

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

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

var arr2 = string.Unformat(format, res)

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

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

Ответы [ 6 ]

15 голосов
/ 13 ноября 2009

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

Один метод - это сообщение в блоге , написанное моим другом. Он реализовал метод расширения под названием 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;
        }
    }
}
10 голосов
/ 11 сентября 2009

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

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

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

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

2 голосов
/ 11 сентября 2009

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

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

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

2 голосов
/ 11 сентября 2009

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

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

Как бы вы "Unformat" это?

1 голос
/ 11 сентября 2009

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

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

Это разрешит неоднозначности в кратчайшие сроки.

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

0 голосов
/ 11 сентября 2009

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

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

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

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; 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...