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

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

Вот пример содержимого файла:

###Myrman###
360 = { # DUPLICATE §§§§§§
    name = "of Myr"
    culture = myrman
}
300507 = {
    name = "of Myr"
    culture = myrman
}
300525 = {
    name = "Trellos"
    culture = myrman
}
300534 = {
    name = "Uteuran"
    culture = myrman
}

##Lysene##
1386 = {
    name = "Ormollen"
    culture = lysene
    coat_of_arms = {
        template = 0
        layer = {
            texture = 14
            texture_internal = 9
            emblem = 0
            color = 0
            color = 0
            color = 0
        }
    }
}
300505 = {
    name = "of Lys"
    culture = lysene
}
300523 = {
    name = "Lohar"
    culture = lysene
}
300532 = {
    name = "Assadyrn"
    culture = lysene
}

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

Myrman: ["of Myr", "of Myr", "Trellos", "Uteuran"]

Lysene: ["Ormollen", "of Lys", "Lohar", "Assadyrn"]

Как мне это сделать с python и его библиотекой регулярных выражений?

Где ты взял этот файл? Очевидно, что это структурированный формат, поэтому было бы проще найти для него синтаксический анализатор, чем пытаться собрать что-то вместе с регулярными выражениями.

AKX 16.02.2023 13:16

Какое расширение у файла?

Tom McLean 16.02.2023 13:25

Привет, это обычный текстовый файл. Это файлы из мода для игры "Crusader Kings 2", но эти файлы структурированы так же, как и обычные файлы игры.

Keenonthedaywalk 16.02.2023 13:29
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
75
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

О, проблема с парсером! Давайте воспользуемся генератором парсеров lark, чтобы разобраться в этом.

Во-первых, давайте создадим синтаксис для нашего файла — он собран на основе примера парсера JSON:

import lark

parser = lark.Lark(r"""
start: (term)*
term: key " = " value "\n"
key: CNAME | SIGNED_NUMBER
value: CNAME | SIGNED_NUMBER | ESCAPED_STRING | map
map: "{" (term)* "}"
%import common.CNAME
%import common.ESCAPED_STRING
%import common.SIGNED_NUMBER
%import common.WS
%ignore WS
%ignore /#.*/
""")

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

Затем давайте напишем преобразователь для преобразования дерева синтаксического анализа Lark в словарь:

class TreeTransformer(lark.Transformer):
    def start(self, items):
        return dict(items)

    def term(self, items):
        return (items[0], items[1])

    def CNAME(self, item):
        return item.value

    def SIGNED_NUMBER(self, item):
        return int(item.value)

    def ESCAPED_STRING(self, item):
        return item.value[1:-1]

    def map(self, items):
        return dict(items)

    def key(self, item):
        return item[0]

    def value(self, item):
        return item[0]

Возможно, можно было бы быть более кратким, но это работает. Давайте запустим его против данных:

from pathlib import Path
from pprint import pprint

data = Path("./so75472097-data.txt").read_text()
tree = parser.parse(data)
res = TreeTransformer().transform(tree)
pprint(res)

Выход

{360: {'culture': 'myrman', 'name': 'of Myr'},
 1386: {'coat_of_arms': {'layer': {'color': 0,
                                   'emblem': 0,
                                   'texture': 14,
                                   'texture_internal': 9},
                         'template': 0},
        'culture': 'lysene',
        'name': 'Ormollen'},
 300505: {'culture': 'lysene', 'name': 'of Lys'},
 300507: {'culture': 'myrman', 'name': 'of Myr'},
 300523: {'culture': 'lysene', 'name': 'Lohar'},
 300525: {'culture': 'myrman', 'name': 'Trellos'},
 300532: {'culture': 'lysene', 'name': 'Assadyrn'},
 300534: {'culture': 'myrman', 'name': 'Uteuran'}}

-- выглядит многообещающе!

Тогда это просто вопрос обхода dict:

from collections import defaultdict
names_by_culture = defaultdict(list)
for info in res.values():
    names_by_culture[info["culture"]].append(info["name"])
pprint(dict(names_by_culture))

... и вуаля!

{'lysene': ['Ormollen', 'of Lys', 'Lohar', 'Assadyrn'],
 'myrman': ['of Myr', 'of Myr', 'Trellos', 'Uteuran']}

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

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

РЕДАКТИРОВАТЬ 2

Как обсуждалось в комментариях, грамматика и преобразователь также подходят для «практически всего» для значений без кавычек:

parser = lark.Lark(r"""
start: (term)*
term: key " = " value "\n"
key: KEYNAME | SIGNED_NUMBER
value: VALUENAME | SIGNED_NUMBER | ESCAPED_STRING | map
map: "{" (term)* "}"
VALUENAME: /[a-zA-Z][^\s=]*/
KEYNAME: /[a-zA-Z][-a-zA-Z0-9_]*/
%import common.ESCAPED_STRING
%import common.SIGNED_NUMBER
%import common.WS
%ignore WS
%ignore /#.*/
""")

from operator import itemgetter, attrgetter


class TreeTransformer(lark.Transformer):
    start = dict
    map = dict
    key = itemgetter(0)
    value = itemgetter(0)
    VALUENAME = attrgetter("value")
    KEYNAME = attrgetter("value")
    term = tuple

    def SIGNED_NUMBER(self, item):
        return int(item.value)

    def ESCAPED_STRING(self, item):
        return item.value[1:-1]

Эй, во-первых, я хочу сказать спасибо за ваше очень подробное объяснение и помощь, я очень благодарен! В любом случае, я проверил ваш код, и он работает, но у меня есть только один вопрос: как я могу позволить этому синтаксическому анализатору анализировать имена со специальными символами, такими как: Crom-Ya или K'Mool? Эти имена есть в файле и я получил ошибку при его запуске, после их удаления все заработало, но как мне разобрать имена с такими спецсимволами?

Keenonthedaywalk 16.02.2023 15:51

Кроме того, я также проверю те синтаксические анализаторы, которые вы предложили, но я согласен, что писать свой собственный код гораздо веселее, ха-ха

Keenonthedaywalk 16.02.2023 15:51

Если они не заключены в кавычки, вам нужно настроить это, чтобы использовать для них что-то другое, кроме CNAME - может быть, просто определить тип терминала NAME: /\w+/?

AKX 16.02.2023 16:31

@Keenonthedaywalk Я редактировал что-то, что может иметь дело, например. culture = ly'sene :)

AKX 16.02.2023 19:55

Эй, мужик, спасибо за всю помощь и усилия с этим постом, я очень ценю это!

Keenonthedaywalk 18.02.2023 11:40

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

result = re.findall('name[ ]*=[ ]*"([A-z ]+)"\n[ ]+culture[ ]*=[ ]*([A-z]+)', a)

names_by_culture = {}
for i in result:
    name = i[0]
    culture = i[1] 
    try:
        names_by_culture[culture].append(name)
    except:
        names_by_culture[culture] = []
        names_by_culture[culture].append(name)
        
print(names_by_culture)

Выход:

{'myrman': ['of Myr', 'of Myr', 'Trellos', 'Uteuran'],
 'lysene': ['Ormollen', 'of Lys', 'Lohar', 'Assadyrn']}

Привет, я отметил сообщение пользователя AKX выше как решенное, но это также очень полезно. Я очень ценю помощь!

Keenonthedaywalk 16.02.2023 15:55

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

Как проанализировать строку User-Agent с помощью Powershell Match, чтобы получить браузер и ОС
Одно регулярное выражение с несколькими шаблонами соответствия
Чтение чего-либо между {{ и }} regex в JavaScript
Почему это регулярное выражение терпит неудачу, когда есть лишний ноль?
Почему оператор bash «=~» игнорирует последнюю часть указанного шаблона?
Разобрать отформатированную строку с метками, написанными ЗАГЛАВНЫМИ БУКВАМИ, за которыми следует их значение, для создания ассоциативного массива
Разделить строку с помощью разделителей и поместить их в несколько строк в sql, не работающем
Регулярное выражение с флагом m в Perl и Python
Как выполнить разделение строк, используя регулярное выражение в качестве ссылки, и чтобы часть используемого шаблона разделителя не удалялась из следующей строки?
Правильный шаблон регулярного выражения (re) в python