Строковый парсинг в C#

В форме чего самый эффективный путь состоит в том, чтобы проанализировать строку C#

"(params (abc 1.3)(sdc 2.0)(www 3.05)....)"

в структуру в форме

struct Params
{
  double abc,sdc,www....;
}

Спасибо

ОТРЕДАКТИРУЙТЕ структуру, всегда имеют те же параметры (те же имена, только удваивается, известный во время компиляции).. но порядок не предоставляют.. только одна структура за один раз..

5
задан Betamoo 3 May 2010 в 18:41
поделиться

7 ответов

В зависимости от вашей полной грамматики у вас есть несколько вариантов: если это очень простая грамматика, и вам не нужно проверять ее на наличие ошибок, вы можете просто воспользоваться следующим (что будет быстро)

var input = "(params (abc 1.3)(sdc 2.0)(www 3.05)....)";
var tokens = input.Split('(');
var typeName = tokens[0];
//you'll need more than the type name (assembly/namespace) so I'll leave that to you
Type t = getStructFromType(typeName);
var obj = TypeDescriptor.CreateInstance(null, t, null, null);
for(var i = 1;i<tokens.Length;i++)
{
    var innerTokens = tokens[i].Trim(' ', ')').Split(' ');
    var fieldName = innerTokens[0];
    var value = Convert.ToDouble(innerTokens[1]);
    var field = t.GetField(fieldName);
    field.SetValue(obj, value);
}

этот простой подход требует, однако, хорошо соответствующей строки, иначе она будет вести себя неправильно.

Если грамматика немного сложнее, например nested (), то этот простой подход не сработает.

вы можете попробовать использовать regEx, но это все еще требует довольно простой грамматики, поэтому, если у вас сложная грамматика, лучшим выбором будет настоящий синтаксический анализатор. Ирония проста в использовании, поскольку вы можете написать все это на простом C # (хотя некоторые знания BNF являются плюсом).

2
ответ дан 13 December 2019 в 22:03
поделиться

Вам нужно поддерживать несколько структур? Другими словами, должно ли это быть динамичным; или вы знаете определение структуры во время компиляции?

Разбор строки с помощью регулярного выражения был бы очевидным выбором.

Вот регулярное выражение, которое проанализирует ваш строковый формат:

private static readonly Regex regParser = new Regex(@"^\(params\s(\((?<name>[a-zA-Z]+)\s(?<value>[\d\.]+)\))+\)$", RegexOptions.Compiled);

Выполнение этого регулярного выражения в строке даст вам две группы с именами «имя» и «значение». Свойство Capture каждой группы будет содержать имена и значения.

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

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

С какой частью у вас проблемы?

2
ответ дан 13 December 2019 в 22:03
поделиться

Вы можете рассмотреть возможность выполнения операций со строкой, достаточных для того, чтобы ввод выглядел как стандартные аргументы командной строки, а затем использовать стандартный синтаксический анализатор аргументов командной строки, например NDesk.Options , для заполнения объекта Params. Вы теряете некоторую эффективность, но компенсируете ее ремонтопригодностью.

public Params Parse(string input)
{
    var @params = new Params();
    var argv = ConvertToArgv(input);
    new NDesk.Options.OptionSet
        {
            {"abc=", v => Double.TryParse(v, out @params.abc)},
            {"sdc=", v => Double.TryParse(v, out @params.sdc)},
            {"www=", v => Double.TryParse(v, out @params.www)}
        }
        .Parse(argv);

    return @params;
}

private string[] ConvertToArgv(string input)
{
    return input
        .Replace('(', '-')
        .Split(new[] {')', ' '});
}
1
ответ дан 13 December 2019 в 22:03
поделиться
using System;

namespace ConsoleApplication1
{
    class Program
    {
        struct Params
        {
            public double abc, sdc;
        };

        static void Main(string[] args)
        {
            string s = "(params (abc 1.3)(sdc 2.0))";
            Params p = new Params();
            object pbox = (object)p; // structs must be boxed for SetValue() to work

            string[] arr = s.Substring(8).Replace(")", "").Split(new char[] { ' ', '(', }, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < arr.Length; i+=2)
                typeof(Params).GetField(arr[i]).SetValue(pbox, double.Parse(arr[i + 1]));
            p = (Params)pbox;
            Console.WriteLine("p.abc={0} p.sdc={1}", p.abc, p.sdc);
        }
    }
}

Примечание: если вы использовали класс вместо структуры, упаковка / распаковка не потребовалась бы.

3
ответ дан 13 December 2019 в 22:03
поделиться

Регулярное выражение может сделать эту работу за вас:

public Dictionary<string, double> ParseString(string input){
    var dict = new Dictionary<string, double>();
    try
    {
        var re = new Regex(@"(?:\(params\s)?(?:\((?<n>[^\s]+)\s(?<v>[^\)]+)\))");
        foreach (Match m in re.Matches(input))
            dict.Add(m.Groups["n"].Value, double.Parse(m.Groups["v"].Value));
    }
    catch
    {
        throw new Exception("Invalid format!");
    }
    return dict;
}

используйте его как:

string str = "(params (abc 1.3)(sdc 2.0)(www 3.05))";
var parsed = ParseString(str);

// parsed["abc"] would now return 1.3

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

Кроме того, я предположил, что входная строка всегда имеет именно тот формат, который вы опубликовали.

2
ответ дан 13 December 2019 в 22:03
поделиться

Я бы просто сделал базовый синтаксический анализатор с рекурсивным спуском. Он может быть более общим, чем вы хотите, но ничто другое не будет намного быстрее.

0
ответ дан 13 December 2019 в 22:03
поделиться

Хотите ли вы создать представление данных вашего определенного синтаксиса?

Если вы ищете легкую сопровождаемость, без необходимости писать длинные операторы RegEx, вы можете создать свой собственный синтаксический анализатор Lexer. вот предыдущее обсуждение на SO с хорошими ссылками в ответах, чтобы помочь вам

Бедный мужской "лексер" для C#

0
ответ дан 13 December 2019 в 22:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: