Форматирование строки в формате Parsing

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

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

  • У меня есть строка для 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 для получения массива) и запустить ее для каждого заполнителя в строке формата. Возможно ли это или есть другое более эффективное решение?

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

Как бы вы "неформатировали" его?

Ответ 4

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

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

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

Ответ 5

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

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

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

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

Ответ 6

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

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; 
}