Мне нужно разобрать файл журнала, и я не знаю, как лучше всего взять разные части каждой строки. Проблема, с которой я столкнулся, заключается в том, что первоначальный разработчик использовал «:» для разграничения токенов, что было немного идиотским, поскольку строка содержит метку времени, которая сама содержит «:»!
Пример строки выглядит примерно так:
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]
У меня нет проблем с чтением файла журнала и доступом к каждой строке, но я не знаю, как разобрать фрагменты?
^(.*?)\:\[(.*?)\]:(.*?)\:(.*?)\:\d+\:(.*?)\:(\[.*?\])$
? Разделите первую группу на |
, если это необходимо.
Не очень хорошо разбирается в RE, как разделить 1-ю группу по '|'?
Предположим, что 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]
на самом деле находится внутри квадратных скобок. Измените по мере необходимости
Обратите внимание, что Group[0]
содержит захват, который здесь является полной строкой. Итак, ваши результаты включены в Group[1]
–Group[6]
.
Спасибо. 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].Значение; <-- это показывает содержимое "данных"
Да, извините, я написал это здесь. Длина данных не входит в группу захвата. Используйте это: "^(.*?)\:\[(.*?)\]:(.*?)\:(.*?)\:(\d+)\:(.*?)\:(\[.*?\])$"
Идеально! Пожалуйста, измените свой комментарий (ы), чтобы ответить, и я отмечу его.
Используя 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; }
}
}
Спасибо за ответ. Данные поступают из текстового файла (файл журнала); также я не контролирую его содержимое, поэтому не могу изменить двоеточие на точку.
Я сделал это в коде, поэтому вам не нужно менять ввод. Мне нужно было заменить двоеточие точкой, чтобы проанализировать строку Date в объекте DateTime.
Я понимаю; Благодарю. Я попробую. Еще раз спасибо за быстрый ответ.
Поскольку входная строка не совсем разделяемый, поскольку разделитель 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 для подробного описания использования и значения каждого токена.
Отметка времени имеет фиксированную длину. Если формат согласован, то вы можете взять первые N символов (разобрать дату и время, используя формат), пропустить 1 (проверить, что это
|
), взять следующие N символов (разобрать дату и время, используя формат), пропустить 1 символ (проверить, что это:
), брать символы до:
(анализировать как IP-адрес) и так далее.