C # Шаблон разбора и сопоставления с текстовым файлом - PullRequest
0 голосов
/ 25 июня 2011

Нужны идеи как решить эту проблему.У меня есть файл шаблона, который описывает строку в текстовом файле.Например:

Шаблон

[%f1%]|[%f2%]|[%f3%]"[%f4%]"[%f5%]"[%f6%]

Текстовый файл

1234|1234567|123"12345"12"123456

Теперь мне нужно прочитать в полях из текстового файла.В файле шаблона поля описаны с [%some name%].Кроме того, в файле шаблона указано, что такое разделители полей, в этом примере здесь есть | и ".Длина полей может меняться в разных файлах, но разделители не изменятся.Как лучше всего читать по шаблону и по шаблону читать в текстовом файле?

РЕДАКТИРОВАТЬ: текстовый файл состоит из нескольких строк, например:

1234|1234567|123"12345"12"123456"\r\n
1234|field|123"12345"12"asdasd"\r\n
123sd|1234567|123"asdsadf"12"123456"\r\n
45gg|somedata|123"12345"12"somefield"\r\n

EDIT2: Хорошо, давайте сделаем его еще сложнее.Некоторые поля могут содержать двоичные данные, и я знаю начальную и конечную позицию поля двоичных данных.Я должен иметь возможность пометить эти поля в шаблоне, и тогда парсер будет знать, что это поле является двоичным.Как решить эту проблему?

Ответы [ 4 ]

1 голос
/ 25 июня 2011

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

class Parser
{
    private static readonly Regex TemplateRegex =
        new Regex(@"\[%(?<field>[^]]+)%\](?<delim>[^[]+)?");

    readonly List<string> m_fields = new List<string>();
    private readonly Regex m_textRegex;

    public Parser(string template)
    {
        var textRegexString = '^' + TemplateRegex.Replace(template, Evaluator) + '$';
        m_textRegex = new Regex(textRegexString);
    }

    string Evaluator(Match match)
    {
        // add field name to collection and create regex for the field
        var fieldName = match.Groups["field"].Value;
        m_fields.Add(fieldName);
        string result = "(.*?)";

        // add delimiter to the regex, if it exists
        // TODO: check, that only last field doesn't have delimiter
        var delimGroup = match.Groups["delim"];
        if (delimGroup.Success)
        {
            string delim = delimGroup.Value;
            result += Regex.Escape(delim);
        }
        return result;
    }

    public IDictionary<string, string> Parse(string text)
    {
        var match = m_textRegex.Match(text);
        var groups = match.Groups;

        var result = new Dictionary<string, string>(m_fields.Count);

        for (int i = 0; i < m_fields.Count; i++)
            result.Add(m_fields[i], groups[i + 1].Value);

        return result;
    }
}
1 голос
/ 25 июня 2011

Вы можете разобрать шаблон с помощью регулярных выражений.Подобное выражение будет соответствовать каждому определению поля и разделителю:

Match m = Regex.Match(template, @"^(\[%(?<name>.+?)%\](?<separator>.)?)+$")

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

Затем можно выполнить итерацию по захватам и использовать результаты для извлечения полей из входной строки и сохранения значений, например, так::

if( m.Success )
{
    Group name = m.Groups["name"];
    Group separator = m.Groups["separator"];
    int index = 0;
    Dictionary<string, string> fields = new Dictionary<string, string>();
    for( int x = 0; x < name.Captures.Count; ++x )
    {
        int separatorIndex = input.Length;
        if( x < separator.Captures.Count )
            separatorIndex = input.IndexOf(separator.Captures[x].Value, index);
        fields.Add(name.Captures[x].Value, input.Substring(index, separatorIndex - index));
        index = separatorIndex + 1;
    }
    // Do something with results.
}

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

0 голосов
/ 25 июня 2011

1 - используйте API для этого sscanf(line, format, __arglist) отметьте здесь

2- Использовать разделение строк Как:

public IEnumerable<int> GetDataFromLines(string[] lines)
{ 
    //handle the output data
    List<int> data = new List<int>();

    foreach (string line in lines)
    {
        string[] seperators = new string[] { "|", "\"" };

        string[] results = line.Split(seperators, StringSplitOptions.RemoveEmptyEntries);

        foreach (string result in results)
        {
            data.Add(int.Parse(result));
        }
    }

    return data;
}

Проверьте это с помощью строки:

line = "1234|1234567|123\"12345\"12\"123456";
string[] lines = new string[] { line };

GetDataFromLines(lines);

//output list items are:
1234
1234567
123
12345
12
123456
0 голосов
/ 25 июня 2011

Я бы сделал это с помощью нескольких строк кода.Переберите строку шаблона, перехватывая весь текст между «[» как именем переменной и всем остальным как терминатором.Прочитать весь текст на терминале, присвоить его имени переменной, повторить.

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