String.Format count количество ожидаемых аргументов - PullRequest
11 голосов
/ 14 февраля 2011

Можно ли посчитать количество ожидаемых аргументов / параметров в строке для String.Format()?

Например: "Hello {0}. Bye {1}" должен вернуть счет 2.

Мне нужно отобразить ошибку, прежде чем string.Format() сгенерирует исключение.

Спасибо за вашу помощь.

Ответы [ 2 ]

13 голосов
/ 14 февраля 2011

Вы можете использовать регулярное выражение, что-то вроде {(. *?)}, А затем просто считать совпадения. Если вам нужно обрабатывать такие случаи, как {0} {0} (который, я думаю, должен возвращать 1), тогда это немного усложняет задачу, но вы всегда можете поместить все совпадения в список и сделать выбор Linq отличным от него , Я думаю что-то вроде кода ниже:

var input = "{0} and {1} and {0} and {2:MM-dd-yyyy}";
var pattern = @"{(.*?)}";
var matches = Regex.Matches(input, pattern);
var totalMatchCount = matches.Count;
var uniqueMatchCount = matches.OfType<Match>().Select(m => m.Value).Distinct().Count();
Console.WriteLine("Total matches: {0}", totalMatchCount);
Console.WriteLine("Unique matches: {0}", uniqueMatchCount);

EDIT:

Я хотел бы ответить на некоторые вопросы, поднятые в комментариях. Обновленный код, размещенный ниже, обрабатывает случаи, когда существуют экранированные скобочные последовательности (т. Е. {{5}}), когда параметры не указаны, а также возвращает значение самого высокого параметра + 1. Код предполагает, что входные строки будут быть хорошо сформированным, но этот компромисс может быть приемлемым в некоторых случаях. Например, если вы знаете, что входные строки определены в приложении и не сгенерированы пользовательским вводом, тогда обработка всех крайних случаев может не потребоваться. Также может быть возможно проверить все сообщения об ошибках, которые будут сгенерированы, используя модульный тест. Что мне нравится в этом решении, так это то, что оно, скорее всего, будет обрабатывать подавляющее большинство брошенных в него строк, и это более простое решение, чем указанное здесь (что предполагает повторную реализацию строки .AppendFormat). Я бы учел тот факт, что этот код может не обрабатывать все крайние случаи, используя try-catch и просто возвращая «Неверный шаблон сообщения об ошибке» или что-то в этом роде.

Одним из возможных улучшений для приведенного ниже кода было бы обновление регулярного выражения, чтобы оно не возвращало начальные символы "{". Это исключило бы необходимость замены ("{", string.Empty). Опять же, этот код может быть не идеальным во всех случаях, но я чувствую, что он адекватно отвечает на вопрос в ответ на вопрос.

const string input = "{0} and {1} and {0} and {4} {{5}} and {{{6:MM-dd-yyyy}}} and {{{{7:#,##0}}}} and {{{{{8}}}}}";
//const string input = "no parameters";
const string pattern = @"(?<!\{)(?>\{\{)*\{\d(.*?)";
var matches = Regex.Matches(input, pattern);
var totalMatchCount = matches.Count;
var uniqueMatchCount = matches.OfType<Match>().Select(m => m.Value).Distinct().Count();
var parameterMatchCount = (uniqueMatchCount == 0) ? 0 : matches.OfType<Match>().Select(m => m.Value).Distinct().Select(m => int.Parse(m.Replace("{", string.Empty))).Max() + 1;
Console.WriteLine("Total matches: {0}", totalMatchCount);
Console.WriteLine("Unique matches: {0}", uniqueMatchCount);
Console.WriteLine("Parameter matches: {0}", parameterMatchCount);
3 голосов
/ 14 февраля 2011

Я думаю, что это будет обрабатывать Escape-скобки и 0: 0000 вещи ... даст наибольшее значение в скобках ... поэтому в моем примере даст 1.

       //error prone to malformed brackets...
        string s = "Hello {0:C} Bye {1} {0} {{34}}";

        int param = -1;
        string[] vals = s.Replace("{{", "").Replace("}}", "").Split("{}".ToCharArray());
        for (int x = 1; x < vals.Length-1; x += 2)
        {
            int thisparam;
            if (Int32.TryParse(vals[x].Split(',')[0].Split(':')[0], out thisparam) && param < thisparam)
                param = thisparam;
        }
        //param will be set to the greatest param now.
...