Файл журнала синтаксического анализа, неоднозначный разделитель

Мне нужно разобрать файл журнала, и я не знаю, как лучше всего взять разные части каждой строки. Проблема, с которой я столкнулся, заключается в том, что первоначальный разработчик использовал «:» для разграничения токенов, что было немного идиотским, поскольку строка содержит метку времени, которая сама содержит «:»!

Пример строки выглядит примерно так:

transaction_date_time:[systemid]:sending_system:receiving_system:data_length:data:[ws_name]

2019-05-08 15:03:13:494|2019-05-08 15:03:13:398:[192.168.1.2]:ABC:DEF:67:cd71f7d9a546ec2b32b,AACN90012001000012,OPNG:[WebService.SomeName.WebServiceModule::WebServiceName]

У меня нет проблем с чтением файла журнала и доступом к каждой строке, но я не знаю, как разобрать фрагменты?

Отметка времени имеет фиксированную длину. Если формат согласован, то вы можете взять первые N символов (разобрать дату и время, используя формат), пропустить 1 (проверить, что это |), взять следующие N символов (разобрать дату и время, используя формат), пропустить 1 символ (проверить, что это :), брать символы до : (анализировать как IP-адрес) и так далее.

Yeldar Kurmangaliyev 22.05.2019 16:35
^(.*?)\:\[(.*?)\]:(.*?)\:(.*?)\:\d+\:(.*?)\:(\[.*?\])$? Разделите первую группу на |, если это необходимо.
Jimi 22.05.2019 16:49

Не очень хорошо разбирается в RE, как разделить 1-ю группу по '|'?

NoBullMan 22.05.2019 17:22

Предположим, что input — это ваша входная строка, а pattern — регулярное выражение: var result = Regex.Match(input, pattern);. Первая группа содержит 2019-05-08 15:03:13:494|2019-05-08 15:03:13:398. Так что вы можете разделить его с var dates = result.Groups[1].Value.Split('|');. Вы получаете массив строк с двумя датами (конечно, в виде строк). Регулярное выражение предполагает, что, например, [192.168.1.2] на самом деле находится внутри квадратных скобок. Измените по мере необходимости

Jimi 22.05.2019 17:37

Обратите внимание, что Group[0] содержит захват, который здесь является полной строкой. Итак, ваши результаты включены в Group[1]Group[6].

Jimi 22.05.2019 17:45

Спасибо. RE выглядит хорошо, совпадение проходит и возвращает true, но когда я использую Groups[x], число «67» почему-то пропускается. Я получаю метки времени, системный идентификатор, отправляющую и принимающую системы, а затем данные; нет «длины данных», и последний индекс (Группы [7]) оказывается пустым. Я использовал: sTimestamp = match.Groups[1].Value; sSystemID = match.Groups[2].Value; sSendingSys = match.Groups[3].Value; sReceiveSys = match.Groups[4].Value; sDataLnegth = match. .Группы[5].Значение; <-- это показывает содержимое "данных"

NoBullMan 22.05.2019 17:51

Да, извините, я написал это здесь. Длина данных не входит в группу захвата. Используйте это: "^(.*?)\:\[(.*?)\]:(.*?)\:(.*?)\:(\d+)\:(.*?)\:(\[.*?\])$"

Jimi 22.05.2019 17:57

Идеально! Пожалуйста, измените свой комментарий (ы), чтобы ответить, и я отмечу его.

NoBullMan 22.05.2019 18:00
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
0
8
464
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Используя Regex, я смог все разобрать. Похоже, что данные взяты из Excel, потому что доля секунд имеет двоеточие вместо точки. С# не любит двоеточие, поэтому мне пришлось заменить двоеточие точкой. Я также проанализировал справа налево, чтобы обойти проблемы с двоеточием.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;


namespace ConsoleApplication3
{
    class Program1
    {
        const string FILENAME = @"c:\temp\test.txt";
        static void Main(string[] args)
        {
            string line = "";
            int rowCount = 0;
            StreamReader reader = new StreamReader(FILENAME);
            string pattern = @"^(?'time'.*):\[(?'systemid'[^\]]+)\]:(?'sending'[^:]+):(?'receiving'[^:]+):(?'length'[^:]+):(?'data'[^:]+):\[(?'ws_name'[^\]]+)\]";

            while ((line = reader.ReadLine()) != null)
            {
                line = line.Trim();
                if (line.Length > 0)
                {
                    if (++rowCount != 1) //skip header row
                    {
                        Log_Data newRow = new Log_Data();
                        Log_Data.logData.Add(newRow);
                        Match match = Regex.Match(line, pattern, RegexOptions.RightToLeft);

                        newRow.ws_name = match.Groups["ws_name"].Value;
                        newRow.data = match.Groups["data"].Value;
                        newRow.length = int.Parse(match.Groups["length"].Value);
                        newRow.receiving_system = match.Groups["receiving"].Value;
                        newRow.sending_system = match.Groups["sending"].Value;
                        newRow.systemid  = match.Groups["systemid"].Value;
                        //end data is first then start date is second
                        string[] date = match.Groups["time"].Value.Split(new char[] {'|'}).ToArray();
                        string replacePattern = @"(?'leader'.+):(?'trailer'\d+)";
                        string stringDate = Regex.Replace(date[1], replacePattern, "${leader}.${trailer}", RegexOptions.RightToLeft);
                        newRow.startDate = DateTime.Parse(stringDate);
                        stringDate = Regex.Replace(date[0], replacePattern, "${leader}.${trailer}", RegexOptions.RightToLeft);
                        newRow.endDate = DateTime.Parse(stringDate );
                    }
                }
            }


        }
    }
    public class Log_Data
    {
        public static List<Log_Data> logData = new List<Log_Data>();

        public DateTime startDate { get; set; } //transaction_date_time:[systemid]:sending_system:receiving_system:data_length:data:[ws_name]
        public DateTime endDate { get; set; }
        public string systemid { get; set; }
        public string sending_system { get; set; }
        public string receiving_system { get; set; }
        public int length { get; set; }
        public string data { get; set; }
        public string ws_name { get; set; }
    }
}

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

NoBullMan 23.05.2019 14:49

Я сделал это в коде, поэтому вам не нужно менять ввод. Мне нужно было заменить двоеточие точкой, чтобы проанализировать строку Date в объекте DateTime.

jdweng 23.05.2019 14:54

Я понимаю; Благодарю. Я попробую. Еще раз спасибо за быстрый ответ.

NoBullMan 23.05.2019 14:56
Ответ принят как подходящий

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

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

string pattern = @"^(.*?)\|(.*?):\[(.*?)\]:(.*?):(.*?):(\d+):(.*?):\[(.*)\]$";

Это даст вам 8 групп + 1 (Group[0]), которые содержат всю строку.

Используя класс регулярное выражение, просто передайте строку для анализа (с именем line, здесь) и регулярное выражение (с именем pattern) методу Соответствовать(), используя настройки по умолчанию:

var result = Regex.Match(line, pattern);

Свойство Группы.Значение возвращает результат каждой группы захвата. Например, две даты:

var dateEnd = DateTime.ParseExact(result.Groups[1].Value, "yyyy-MM-dd hh:mm:ss:ttt", CultureInfo.InvariantCulture),
var dateStart = DateTime.ParseExact(result.Groups[2].Value, "yyyy-MM-dd hh:mm:ss:ttt", CultureInfo.InvariantCulture),

IpAddress извлекается с помощью: \[(.*?)\].
Вы можете дать имя этой группе, чтобы было понятнее, к чему относится значение. Просто добавьте строку с префиксом ? и заключенную в <> или одинарные кавычки ', чтобы назвать группу:

...\[(?<IpAddress>.*?)\]...

Однако обратите внимание, что присвоение имени группе изменит индексацию Regex.Groups: группы без имени будут вставлены первыми, а группы с именами — после. Таким образом, если назвать только группу IpAddress, она станет последним элементом Groups[8]. Конечно, вы можете назвать все группы, и индексация будет сохранена.

var hostAddress = IPAddress.Parse(result.Groups["IpAddress"].Value);

Этот шаблон должен позволить машине Средняя анализировать 130,000~150,000 строк в секунду.
Вам придется протестировать его, чтобы найти шаблон идеально. Например, первое совпадение (соответствующее первой дате): (.*?)\|, намного быстрее, если не жадное (используя ленивый квантификатор *?). Обратное для последнего матча: \[(.*)\]. Шаблон, используемый йдвенг, даже быстрее, чем тот, который используется здесь.

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

Другие вопросы по теме