Чтение данных с использованием регулярного выражения - PullRequest
0 голосов
/ 12 марта 2020

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

Мои данные:

TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2

Эти данные состоят из 5 важных частей, которые я хочу собрать:

  1. Дата сразу после отметки времени: 1579051725

  2. Num (первая часть чисел до 128, 129, 130 и др. c): .1.3.4.1.2.1.1.1.1.1.1

  3. Num2 (вторая часть): 128 или 129 или 130 или другие в моем большом наборе данных

  4. Syntax: в данном случае это имя: STRING

  5. Counter: в данном случае это строки; AA1 или Eth1 или Eth2

У меня также есть (нужно иметь) константу Num в качестве словаря в программе, которая содержит указанное выше значение и константу syntax

Я хочу прочитать файл данных,

  1. Если Num соответствует моей постоянной в программе,

  2. захват Num2,

  3. проверка соответствия Syntax константе syntax в программе

  4. захват Counter

Когда я говорю «захватить», я имею в виду поместить эти данные в соответствующий словарь.

Короче говоря, я хочу прочитать файл данных, разделить 5 переменных внутри него, сопоставить 2 переменные с постоянными значениями словаря, а также захват и хранение 3 переменных (включая время) в словаре.

У меня проблемы с разделением данных на данный момент. Я могу разделить все, кроме Num и Num2. Также я не уверен, как создавать постоянные словари и как я должен помещать их в постоянные словари.

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

Пока у меня есть следующее:

constant_dic1 = {[".1.3.4.1.2.1.1.1.1.1.1"]["STRING" ]}
data_cols = {'InterfaceNum':[],"IndexNum":[],"SyntaxName":[],"Counter":[],"TimeStamp":[]}
fileN = args.File_Name
with open (fileN, 'r') as f:

    for lines in f:
        if lines.startswith('.'):
            if ': ' in lines:
                lines=lines.split("=")
                first_part = lines[0].split()
                second_part = lines[1].split()
                for i in first_part:
                    f_f = i.split("{}.{}.{}.{}.{}.{}.{}.{}.{}.{}.{}.")
                print (f_f[0])

Когда я запускаю программу, я получаю сообщение об ошибке, что "индексы TypeError: list должны быть целыми числами или кусочки, а не str ".

Когда я закомментирую часть словаря, вывод будет Num, а также Num2. Он не разбивается и не печатает только часть Num.

Любая помощь приветствуется! Если есть какой-либо другой источник, пожалуйста, дайте мне знать ниже. Пожалуйста, дайте мне знать, если мне нужны какие-либо обновления по этому вопросу без голосования. Спасибо!

ОБНОВЛЕННЫЙ КОД

import pandas as pd
import io
import matplotlib
matplotlib.use('TkAgg') # backend option for matplotlib #TkAgg #Qt4Agg #Qt5Agg
import matplotlib.pyplot as plt
import re # regular expression
import argparse # for optional arguments
parser = argparse.ArgumentParser()
parser.add_argument('File_Name', help="Enter the file name | At least one file is required to graph")
args=parser.parse_args()

data_cols = {'InterfaceNum':[],"IndexNum":[],"SyntaxName":[],"Counter":[],"TimeStamp":[]}
fileN = args.File_Name
input_data = fileN
expr = r"""
    TIMESTAMP:\s(\d+)           # date    - TimeStamp
    |                           # ** OR **
    ((?:\.\d+)+)                # num     - InterfaceNum
        \.(\d+)\s=\s            # num2    - IndexNum
            (\w+):\s            # syntax  - SyntaxName
                (\w+)           # counter - Counter
    """
expr = re.compile(expr, re.VERBOSE)
data = {}
keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']


with io.StringIO(input_data) as data_file:
    for line in data_file:
        try:
            find_data = expr.findall(line)[0]
            vals = [date, num, num2, syntax, counter] = list(find_data)
            if date:
                cur_date = date
                data[cur_date] = {k: [] for k in keys}
            elif num:
                vals[0] = cur_date
                for k, v in zip(keys, vals):
                    data[cur_date][k].append(v)
        except IndexError:
            # expr.findall(...)[0] indexes an empty list when there's no
            # match.
            pass

data_frames = [pd.DataFrame.from_dict(v) for v in data.values()]

print(data_frames[0])

ОШИБКА I GET

Traceback (most recent call last):
  File "v1.py", line 47, in <module>
    print(data_frames[0])
IndexError: list index out of range


НОВЫЕ ДАННЫЕ

TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2
.1.2.3.4.5.6.7.8.9.10.11.131 = INT32: A

ОБНОВЛЕННЫЙ КОД (v2)

import pandas as pd
import io
import matplotlib
import re # regular expression

file = r"/home/rusif.eyvazli/Python_Projects/network-switch-packet-loss/s_data.txt"



def get_dev_data(file_path, timestamp=None, iface_num=None, idx_num=None, 
                 syntax=None, counter=None):

    timestamp = timestamp or r'\d+'
    iface_num = iface_num or r'(?:\.\d+)+'
    idx_num   = idx_num   or r'\d+'
    syntax    = syntax    or r'\w+'
    counter   = counter   or r'\w+'

#     expr = r"""
#         TIMESTAMP:\s({timestamp})   # date    - TimeStamp
#         |                           # ** OR **
#         ({iface_num})               # num     - InterfaceNum
#             \.({idx_num})\s=\s      # num2    - IndexNum
#                 ({syntax}):\s       # syntax  - SyntaxName
#                     ({counter})     # counter - Counter
#         """

    expr = r"TIMESTAMP:\s(\d+)|((?:\.\d+)+)\.(\d+)\s=\s(\w+):\s(\w+)"

#   expr = re.compile(expr, re.VERBOSE)

    expr = re.compile(expr)

    rows = []
    keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']
    cols = {k: [] for k in keys}

    with open(file_path, 'r') as data_file:
        for line in data_file:
            try:

                find_data = expr.findall(line)[0]
                vals = [tstamp, num, num2, sntx, ctr] = list(find_data)
                if tstamp:
                    cur_tstamp = tstamp
                elif num:
                    vals[0] = cur_tstamp
                    rows.append(vals)
                    for k, v in zip(keys, vals):
                         cols[k].append(v)
            except IndexError:
                # expr.findall(line)[0] indexes an empty list when no match.
                pass

    return rows, cols

const_num    = '.1.3.4.1.2.1.1.1.1.1.1'
const_syntax = 'STRING'

result_5 = get_dev_data(file)

# Use the results of the first dict retrieved to initialize the master
# dictionary.
master_dict = result_5[1]

df = pd.DataFrame.from_dict(master_dict)

df = df.loc[(df['InterfaceNum'] == '.1.2.3.4.5.6.7.8.9.10.11') & (df['SyntaxName'] == 'INT32' )] 

print(f"\n{df}")

ВЫХОД

    TimeStamp              InterfaceNum IndexNum SyntaxName Counter
3  1579051725  .1.2.3.4.5.6.7.8.9.10.11      131      INT32       A

Ответы [ 2 ]

2 голосов
/ 13 марта 2020

Анализ ввода необработанных файлов с использованием регулярных выражений

Следующая функция представляет собой пример анализа ввода необработанных файлов с регулярными выражениями.

Группы захвата регулярных выражений зациклены для создания записей. Это шаблон многократного использования, который можно применять во многих случаях. Подробнее о том, как это работает, можно узнать в разделе «Группировка в составных регулярных выражениях».

Функция отфильтрует записи, соответствующие значениям параметра. Оставляя им значения по умолчанию, функция возвращает все строки данных.

def get_dev_data(file_path, timestamp=None, iface_num=None, idx_num=None, 
                 syntax=None, counter=None):
    timestamp = timestamp or r'\d+'
    iface_num = iface_num or r'(?:\.\d+)+'
    idx_num   = idx_num   or r'\d+'
    syntax    = syntax    or r'\w+'
    counter   = counter   or r'\w+'
    expr = rf"""
        TIMESTAMP:\s({timestamp})   # date    - TimeStamp
        |                           # ** OR **
        ({iface_num})               # num     - InterfaceNum
            \.({idx_num})\s=\s      # num2    - IndexNum
                ({syntax}):\s       # syntax  - SyntaxName
                    ({counter})     # counter - Counter
        """
    expr = re.compile(expr, re.VERBOSE)
    rows = []
    keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']
    cols = {k: [] for k in keys}

    with open(file_path, 'r') as data_file:
        for line in data_file:
            try:
                find_data = expr.findall(line)[0]
                vals = [tstamp, num, num2, sntx, ctr] = list(find_data)
                if tstamp:
                    cur_tstamp = tstamp
                elif num:
                    vals[0] = cur_tstamp
                    rows.append(vals)
                    for k, v in zip(keys, vals):
                        cols[k].append(v)
            except IndexError:
                # expr.findall(line)[0] indexes an empty list when no match.
                pass
    return rows, cols

Возвращается кортеж. Первый элемент, rows, представляет собой список строк данных в простом формате; второй элемент, cols, представляет собой словарь, снабженный ключом по имени столбца со списком данных строк на ключ. Оба содержат одни и те же данные и могут быть усвоены Pandas с pd.DataFrame.from_records() или pd.DataFrame.from_dict() соответственно.

пример фильтрации

Это показывает, как записи могут быть отфильтрованы используя параметры функции. Я думаю, что последний, result_4, соответствует описанию в вопросе. Предположим, что для iface_num установлены ваши const_num, а для syntax - ваши const_syntax значения. Будут возвращены только совпадающие записи.

if __name__ == '__main__':

    file = r"/test/inputdata.txt"

    result_1 = get_dev_data(file)[0]
    result_2 = get_dev_data(file, counter='Eth2')[0]
    result_3 = get_dev_data(file, counter='Eth2|AA1')[0]
    result_4 = get_dev_data(file,
                           iface_num='.1.3.4.1.2.1.1.1.1.1.1', syntax='STRING')[0]

    for var_name, var_val in zip(['result_1', 'result_2', 'result_3', 'result_4'],
                                 [ result_1,   result_2,   result_3,   result_4]):

        print(f"{var_name} = {var_val}")

Вывод

result_1 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '129', 'STRING', 'Eth1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_2 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_3 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_4 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '129', 'STRING', 'Eth1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]

Используя первый возвращенный элемент кортежа, доступ к данным столбца можно получить из возвращенные записи, используя их смещения. Например, TimeStamp будет доступен как first_item[0][0] - первая строка, первый столбец. Или строки могут быть преобразованы в фрейм данных и доступны таким образом.

Входной файл /test/inputdata.txt

TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2

Преобразование данных строки в Pandas dataframe

Первым элементом кортежа в выходных данных функции будут строки данных, соответствующие столбцам, которые мы определили. Этот формат можно преобразовать в Pandas фрейм данных, используя pd.DataFrame.from_records():

>>> row_data = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1']]]
>>>
>>> column_names = ['TimeStamp', 'InterfaceNum', 'IndexNum', 
...                 'SyntaxName', 'Counter']
>>>
>>> pd.DataFrame.from_records(row_data, columns=column_names)
    TimeStamp            InterfaceNum IndexNum SyntaxName Counter
0  1579051725  .1.3.4.1.2.1.1.1.1.1.1      128     STRING     AA1
>>> 

Преобразовать данные столбца в Pandas фрейм данных

Функция также производит словарь в качестве второго элемента возвращаемого кортежа, содержащего те же данные, который также может создавать тот же кадр данных, используя pd.DataFrame.from_dict().

>>> col_data = {'TimeStamp': ['1579051725'], 
...             'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1'], 
...             'IndexNum': ['128'], 'SyntaxName': ['STRING'], 
...             'Counter': ['AA1']}
>>> 
>>> pd.DataFrame.from_dict(col_data)
    TimeStamp            InterfaceNum IndexNum SyntaxName Counter
0  1579051725  .1.3.4.1.2.1.1.1.1.1.1      128     STRING     AA1
>>> 

Пример словаря

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

const_num    = '.1.3.4.1.2.1.1.1.1.1.1'
const_syntax = 'STRING'

result_5 = get_dev_data(file, iface_num=const_num, syntax=const_syntax)

# Use the results of the first dict retrieved to initialize the master
# dictionary.
master_dict = result_5[1]

print(f"master_dict = {master_dict}")

result_6 = get_dev_data(file, counter='Eth2|AA1')

# Add more records to the master dictionary.
for k, v in result_6[1].items():
    master_dict[k].extend(v)

print(f"master_dict = {master_dict}")

df = pandas.DataFrame.from_dict(master_dict)

print(f"\n{df}")

Вывод

master_dict = {'TimeStamp': ['1579051725', '1579051725', '1579051725'], 'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1'], 'IndexNum': ['128', '129', '130'], 'SyntaxName': ['STRING', 'STRING', 'STRING'], 'Counter': ['AA1', 'Eth1', 'Eth2']}
master_dict = {'TimeStamp': ['1579051725', '1579051725', '1579051725', '1579051725', '1579051725'], 'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1'], 'IndexNum': ['128', '129', '130', '128', '130'], 'SyntaxName': ['STRING', 'STRING', 'STRING', 'STRING', 'STRING'], 'Counter': ['AA1', 'Eth1', 'Eth2', 'AA1', 'Eth2']}

    TimeStamp            InterfaceNum IndexNum SyntaxName Counter
0  1579051725  .1.3.4.1.2.1.1.1.1.1.1      128     STRING     AA1
1  1579051725  .1.3.4.1.2.1.1.1.1.1.1      129     STRING    Eth1
2  1579051725  .1.3.4.1.2.1.1.1.1.1.1      130     STRING    Eth2
3  1579051725  .1.3.4.1.2.1.1.1.1.1.1      128     STRING     AA1
4  1579051725  .1.3.4.1.2.1.1.1.1.1.1      130     STRING    Eth2

Если все столбцы данных словаря не нужны, ключи от него можно обойтись с помощью <dict>.pop(<key>). Или вы можете удалить столбцы из любого фрейма данных, созданного на основе данных.


Группировка в составных регулярных выражениях

Это выражение показывает выражение, которое вычисляется в функции, когда все его параметры имеют значения по умолчанию.

expr = r"""
    TIMESTAMP:\s(\d+)           # date    - TimeStamp
    |                           # ** OR **
    ((?:\.\d+)+)                # num     - InterfaceNum
        \.(\d+)\s=\s            # num2    - IndexNum
            (\w+):\s            # syntax  - SyntaxName
                (\w+)           # counter - Counter
    """

В приведенном выше регулярном выражении есть два альтернативных оператора, разделенных оператором OR, |. Эти альтернативы соответствуют либо строке данных временной метки, либо данным устройства. И в этих подвыражениях есть группировки для захвата определенных c фрагментов строковых данных. Группы соответствия создаются путем помещения круглых скобок (...) вокруг подвыражения. Синтаксис для не группирующих скобок: (?:...).

. Независимо от того, какие альтернативные подвыражения совпадают, все равно будет одинаковое количество групп совпадений, возвращаемых за успешный вызов re.findall(). Может быть, немного нелогично, но так оно и есть.

Однако эта функция облегчает написание кода для извлечения того, какие поля совпадения вы захватили, поскольку вы знаете позиции, в которых должны быть группы, независимо от сопоставленного подвыражения:

     [<tstamp>, <num>, <num2>, <syntax>, <counter>]
     # ^expr1^  ^.............expr2..............^

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

        if tstamp:
            # First expression hit.
        elif num:
            # Second alt expression hit.

Когда выражение совпадает со строкой текста, который имеет временную метку, попадет первое подвыражение, и его группы будут заполнены.

>>> re.findall(expr, "TIMESTAMP: 1579051725 20100114-202845", re.VERBOSE)
[('1579051725', '', '', '', '')]

Здесь первая группировка из выражения заполнена, а остальные группы пусты. Другие группировки принадлежат другому подвыражению.

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

>>> re.findall(expr, ".1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1", re.VERBOSE)
[('', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1')]

И, наконец, когда ни одно из подвыражений не совпадает, тогда все выражение не получает попадания. В этом случае мы получаем пустой список.

>>> re.findall(expr, "ifTb: name-nam-na", re.VERBOSE)
[]
>>> 

Для контраста, вот выражение без подробного синтаксиса и документации:

expr = r"TIMESTAMP:\s(\d+)|((?:\.\d+)+)\.(\d+)\s=\s(\w+):\s(\w+)"
1 голос
/ 12 марта 2020

Пожалуйста, используйте пакет python re для использования регулярного выражения в python. Этот пакет упрощает использование регулярных выражений в python, поэтому вы можете использовать различные функции, которые находятся внутри этого пакета, для достижения того, что вам нужно. https://docs.python.org/3/library/re.html#module -contents используйте эту ссылку для чтения документов.

Существует функция re.Pattern.match (), которая может использоваться для сопоставления шаблонов, так как вам нужно попробовать это вне.

...