Мне нужна помощь с чтением и записью файлов .cfg в Python. Файлы .cfg, которые я хочу читать и записывать, имеют определенный формат, который нельзя изменить.
Пример файла .cfg:
[META]
title = "Xxxx xxxx"
creator = "xxx"
artist = "xxxx"
artist = "xxx xxx"
title = "xxx xxx (xxxxxx)"
length = "4:59"
Некоторые файлы .cfg также могут иметь очень длинные форматы (что мне кажется странным). Это всего лишь фрагмент очень длинного .cfg файла:
[EASY]
bpm = 100
offset = 0
spawn = [63, 111, 161, 201, 285, 339, 342, 347, 380, 388, 422, 449, 470, 507, 511, 531, 551, 555, 583, 591, 634, 638, 642, 701, 783]
half_spawn = [0, 1, 8, 16]
initial_data = {
"type": 1,
"animation": "xxxx.png",
"looping": false,
"fx": "",
"background": [
"xxxx.png",
{
"static": false
}
],
"voice_bank": {
}
}
Я использовал такие библиотеки, как libconf и configparser, но ни одна из них не понимает формат файла и сообщает о многочисленных ошибках.
Больше всего мне повезло, когда я пытался прочитать этот тип файла, используя что-то вроде этого:
lines = open('./test.cfg').read().splitlines()
Но даже это приводит к множеству проблем с форматированием, и с этим трудно работать. Я видел другие сообщения об этом, но они не имеют такого же формата.
Я не очень разбираюсь в Python, любая помощь приветствуется.
Не существует единого стандарта для конфигурационных файлов. Знание того, какая программа читает этот .cfg, может помочь найти решение. Если его читает «foobar», вы можете выполнить поиск в Интернете по «формату файла конфигурации foobar» или что-то в этом роде.
@D.L Я хочу читать и писать в них обоих, но это разные файлы. Второй на самом деле очень большой с кучей значений, но я попытался сделать его коротким, просто чтобы показать формат, который мне нужно было прочитать.
Этот формат файла выглядит странно. Я предполагаю, что вам, возможно, придется создать собственный анализатор для этого типа файлов. Я знаю, что вы упомянули, что вы не можете изменить формат вашего файла, но если вы хотите избежать создания собственного синтаксического анализатора, я думаю, вам нужно будет полностью преобразовать его в более распространенный формат, который поддерживает сложную вложенность, такую как json или ямл.
@tdelaney Программа, которая будет читать это, представляет собой ритмическую игру, которая, насколько мне известно, была создана на игровом движке под названием godot, который, как я полагаю, основан на C.
@frenchytheasian Эта доза звучит как то, что мне, возможно, придется в конечном итоге сделать, хотя я понятия не имею, как это сделать. Поскольку у меня нет возможности изменить формат .cfg, поскольку программа, которую я не могу редактировать, должна его прочитать. Я просто модифицирую игру.
Godot ConfigFile выглядит так, как у вас получилось. Ссылка показывает средства доступа GDScript и CSharp. Не знаю, как это поможет, за исключением того, что, возможно, игра примет GDScript, который вы могли бы использовать. Возможно, отдельная программа CSharp, которая преобразуется во что-то, что может анализировать python. Пусть он пишет на стандартный вывод.
@tdelaney Я не уверен, что вы имеете в виду под стандартным выводом, но большое спасибо, вы поставили меня на правильный путь, чтобы понять это. Я рассмотрю возможность создания отдельной программы на С#, которая либо дозирует все, что я хочу для меня, полностью избавляясь от Python, либо интегрирую C# вместе с моей текущей программой на Python.
Я думал, что это программа C#, которая берет файл конфигурации, создает JSON и записывает его в sys.stdout. Затем с помощью вызова subprocess.run в python вы можете получить JSON, не касаясь жесткого диска с временным файлом. После небольшого поиска кажется, что другие также работали над углом json. Там может быть уже инструмент там.
@tdelaney Это определенно будет проблемой для меня, но я готов к этому. Я попытаюсь сделать, как вы сказали, а также поищу инструмент, который уже может делать то, что я хочу, с частью json.
На самом деле это невозможно сделать только с помощью стандартных инструментов библиотеки, секундочку...






Одним из возможных решений может быть использование библиотеки python-libconf, которая является полностью написанной на Python.
это пример использования:
import libconf
with io.open('file.cfg', encoding='utf-8') as f:
cfg = libconf.load(f)
Я пробовал это раньше, и libconf не может загрузить файл. Просто выдает несколько ошибок.
Как сейчас написано, ваш ответ неясен. Пожалуйста, отредактируйте , чтобы добавить дополнительные сведения, которые помогут другим понять, как это отвечает на заданный вопрос. Вы можете найти больше информации о том, как писать хорошие ответы в справочном центре.
Вы можете написать небольшой конечный автомат для поддержки «сложных» многострочных значений и стандартную библиотеку ast.literal_eval() для преобразования значений в данные Python.
Это не удастся для таких вызовов, как Vector3(0, 0, 0), которые допустимы в файлах Godot, но вы можете довольно легко добавить код, например. пропустите их или используйте, например. библиотека pure-eval для работы с ними.
Предполагая, что переменная data_fp является открытым файлом (я использовал io.StringIO() для тестирования),
import ast
def parse_godot_value(value: str):
# The Godot complex value structure is close enough to Python
# that we can do a couple small modifications
# and then pass it through ast.literal_eval() to get a Python object.
try:
# Try without modifications first – we might get lucky.
return ast.literal_eval(value)
except Exception:
pass
# These should preferably be done by walking the AST and replacing `false` identifiers
# with `False` constants, but this is a quick and dirty solution.
value = value.replace("false", "False")
value = value.replace("true", "True")
value = value.replace("null", "None")
return ast.literal_eval(value)
def parse_godotesque_ini(fp):
current_section = None
complex_key = None
complex_value = None
for line in fp:
line = line.rstrip()
if not line:
continue
if line.startswith("["):
current_section = line.strip("[]")
continue
if complex_value is None: # Not busy parsing a complex value
key, _, value = line.partition(" = ")
key = key.strip()
value = value.strip()
if not (key and value):
continue
if value == "{":
complex_key = key
complex_value = ["{"] # Start of a complex value
else:
yield (current_section, key, parse_godot_value(value))
else: # Busy parsing a complex value
complex_value.append(line)
if line == "}": # End of a complex value
yield (current_section, complex_key, parse_godot_value("\n".join(complex_value)))
complex_key = None
complex_value = None
for section, key, value in parse_godotesque_ini(data_fp):
print(section, key, value)
распечатывает
META title Xxxx xxxx
META creator xxx
META artist xxxx
META artist xxx xxx
META title xxx xxx (xxxxxx)
META length 4:59
EASY bpm 100
EASY offset 0
EASY spawn [63, 111, 161, 201, 285, 339, 342, 347, 380, 388, 422, 449, 470, 507, 511, 531, 551, 555, 583, 591, 634, 638, 642, 701, 783]
EASY half_spawn [0, 1, 8, 16]
EASY initial_data {'type': 1, 'animation': 'xxxx.png', 'looping': False, 'fx': '', 'background': ['xxxx.png', {'static': False}], 'voice_bank': {}}
для данных, которые вы вставили.
Удачи!
какой файл вы пытаетесь прочитать, верхний или нижний?