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

Допустим, у меня есть файл под названием «Еда», в котором перечислены названия некоторых блюд и их цены. Некоторые из этих продуктов являются сырыми ингредиентами, а другие сделаны из разного количества этих ингредиентов. Например, я могу вручную указать цену яиц как 1 и обнаружить, что цена омлета по умолчанию равна 10, но затем обнаружить, что омлет будет только нужно 5 яиц, поэтому мне нужна программа, чтобы прочитать цену яиц, найти строку, содержащую омлет, и заменить ее на «омлет:» + str (5 * яйца). Мне также может понадобиться добавить дополнительные ингредиенты / продукты питания, например. стопка омлетов, состоящая из 5 омлетов. основная цель состоит в том, чтобы сделать возможным простое редактирование значения яиц и обновление значения омлета и кучи омелетов. Я начал код, просто создав список строк, содержащихся в файле.

with open("Food.txt") as g:
    foodlist=g.readlines()

Файл «Food.txt» будет иметь следующий формат:

eggs: 5
omelette: 20
pileofomelettes: 120

и т.д... и после запуска кода он должен выглядеть так

eggs: 5
omelette: 25
pileofomelettes: 125

Я бы закодировал отношения вручную, так как вряд ли они когда-либо изменятся (и даже если бы они изменились, мне было бы довольно легко войти и изменить коэффициенты)

и будет читаться python в формате списка как что-то вроде

'['яйцо 2\n', 'мука 1\n', 'масло 1\n', 'сахар 3\n', 'миндаль 5\n', 'вишня 8\n']'

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

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

РЕДАКТИРОВАТЬ - в реальном приложении программы не имеет значения, в каком порядке элементы перечислены в конечном файле, поэтому, если я перечислил все сырьевые ингредиенты в 1 месте, а все составные элементы в другом (с большое пространство между ними, если нужно добавить больше необработанных элементов), тогда я мог бы просто переписать всю вторую половину файла в произвольном порядке без проблем, пока положение строки сырых ингредиентов остается прежним.


person Cubbs    schedule 14.09.2015    source источник
comment
не могли бы вы включить два исходных файла?   -  person Cody Bouche    schedule 14.09.2015
comment
проходит ли эта цепь? Что, если я определяю стопку омлетов?   -  person Caridorc    schedule 14.09.2015
comment
Я сделал этот пример еды, чтобы помочь мне применить его к реальному примеру в том же формате. И да, я хотел бы иметь возможность определить кучу омлетов, возможно, на более позднем этапе, и просто скопировать некоторый код из более раннего на программу и просто измените содержащиеся в ней аргументы, чтобы получить кучу омлетов   -  person Cubbs    schedule 14.09.2015
comment
В списке также могут быть вещи, которые требуют, чтобы у вас были значения нескольких ингредиентов, например. пирог = мука+масло   -  person Cubbs    schedule 14.09.2015
comment
Проблема здесь в том, что вам нужна какая-то реляционная модель, иначе вам придется жестко закодировать связь между яйцами и омлетом.   -  person Cody Bouche    schedule 14.09.2015
comment
Я не ожидаю, что эта программа будет короткой — в реальном примере, который я делаю (который вычисляет цены предметов в Minecraft на основе ингредиентов, которые они создают) — мне придется написать другой фрагмент кода для всех 173 элементов, для которых я собираюсь написать (или, по крайней мере, я ожидаю, что придется) - мне просто нужно знать, как это сделать для 2 или около того элементов, чтобы я мог начать. Я поищу реляционную модель по мере продвижения и запишу ее, только что использовал омлет в качестве примера простого отношения.   -  person Cubbs    schedule 14.09.2015
comment
хорошо, можете ли вы дать мне ожидаемый результат примера, который у вас есть в исходном сообщении?   -  person Cody Bouche    schedule 14.09.2015
comment
Есть ли в вашем файле точка, которая говорит 5 eggs -> 1 omelette, или вы будете жестко кодировать ее?   -  person Teepeemm    schedule 14.09.2015
comment
@Teepeemm Я буду жестко кодировать это, в Cody Bouche я ожидаю, что строка омлета станет «омлетом: 25», а строка кучи омлетов станет «pileofomelettes: 125».   -  person Cubbs    schedule 14.09.2015
comment
в контексте моей цели рецепты не изменятся, я просто жестко закодирую все отношения.   -  person Cubbs    schedule 14.09.2015


Ответы (3)


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

omelette: 5 x eggs + 1 x onions
pileofomelettes: 6 x omelette

Здесь вы можете поместить произвольное количество ингредиентов типа:

комплекс: число1 x ингредиент1 + число2 x ингредиент2 + ...

и так далее. А в вашем food.txt указаны цены на все ингредиенты и комплексы:

eggs: 2
onions: 1
omelette: 11.0
pileofomelettes: 60

Теперь мы видим, что значение для pileofomlettes намеренно отображается здесь некорректно. Итак, мы запустим код ниже, а также вы можете изменить цифры и посмотреть результаты.

#!/usr/bin/python
''' This program takes in a relations file and a food text files as inputs
and can be used to update the food text file based on changes in either of these'''

relations = {}
foodDict = {}

# Mapping ingredients to each other in the relations dictionary
with open("relations.txt") as r:
    relationlist=r.read().splitlines()
    for relation in relationlist:
        item, equatedTo = relation.split(': ')
        ingredientsAndCoefficients = equatedTo.split(' + ')
        listIngredients = []
        for ingredient in ingredientsAndCoefficients:
            coefficient, item2 = ingredient.split(' x ')
            # A list of sets of amount and type of ingredient
            listIngredients.append((float(coefficient),item2))
        relations.update({item:listIngredients})


# Creating a food dictionary with values from food.txt and mapping to the relations dictionary
with open("food.txt") as g:
    foodlist=g.read().splitlines()
    for item in foodlist:
        food,value = item.split(': ')
        foodDict.update({food:value})
    for food in relations.keys():
        # (Raw) Ingredients with no left hand side value in relations.txt will not change here.
        value = 0.
        for item2 in range(len(relations[food])):
            # Calculating new value for complex here.
            value += relations[food][item2][0]* float(foodDict[relations[food][item2][1]])
        foodDict.update({food: value })

# Altering the food.txt with the new dictionary values 
with open("food.txt",'w') as g:
    for key in sorted(foodDict.keys()):
        g.write(key + ': ' + str (foodDict[key])+ '\n')
        print key + ': ' + str(foodDict[key])

И получается:

eggs: 2
onions: 1
omelette: 11.0
pileofomelettes: 66.0

Вы можете изменить цену на яйца до 5 в файле food.txt и

eggs: 5
onions: 1
omelette: 26.0
pileofomelettes: 156.0
person Sahil M    schedule 14.09.2015
comment
Я полагаю, что мог бы создать файл отношений, но поскольку есть проблема с некоторыми элементами, которые могут потребовать определенного количества одного элемента и определенного количества другого, поэтому я думаю, что я просто выберу более простой вариант ручного кодирования. Вместо этого, если бы я хотел изменить коэффициенты, используемые для расчета обновленных значений, я думаю, что просто зашел бы и отредактировал саму программу так же быстро, как и в отдельный файл .txt, если бы я использовал Ctrl + F. - person Cubbs; 15.09.2015
comment
Вы можете легко изменить это, добавив «+» для всех товаров, которые входят в вашу смесь, и проанализировать файл. Может быть плохой идеей жестко закодировать эти числа, потому что ваш код будет невозможно отлаживать! - person Sahil M; 15.09.2015
comment
Хотя я знаю, что это было бы плохой практикой, я не буду использовать отдельный файл для отношений, я также подчеркнул, что теперь мне не нужно будет выполнять какой-либо поиск и замену. Так что все, что мне действительно нужно знать, это как найти значения в словаре и переписать файл на основе этого (извините, что вы приложили усилия для написания этого кода :(), я посмотрю на это снова завтра и посмотрю, смогу ли я сжать проблему до чего-то с чуть меньшей двусмысленностью. - person Cubbs; 15.09.2015
comment
Хорошо, я прочитал это сейчас, но я внес изменение, о котором говорил. Я надеюсь, что это новое изменение, указанное выше, уже работает для вас. - person Sahil M; 15.09.2015
comment
Теперь работает, просто добавил несколько круглых скобок к этому последнему оператору печати, теперь начнется работа над тем, чтобы заставить его работать на реальном примере :) - person Cubbs; 15.09.2015
comment
Конечно, вы можете написать сюда, если возникнут проблемы, не забудьте отметить ответ, если считаете его полезным. - person Sahil M; 15.09.2015

Как ваша программа узнает компоненты каждого элемента? Я предлагаю вам вести два файла: один со стоимостью атомарных предметов (яиц) и другой с рецептами (омлет ‹= 5 яиц).

Прочитайте оба файла. Сохраните атомарные затраты, помня, сколько у вас есть таких элементов, atomic_count. Расширьте эту таблицу из файла recipes, по одной строке за раз. Если рецепт, который вы читаете, полностью состоит из предметов с известной стоимостью, тогда вычислите стоимость и добавьте этот предмет в список «известных». В противном случае добавьте рецепт в список «позднее» и продолжите.

Когда вы дойдете до конца обоих входных файлов, у вас будет список известных затрат и несколько других рецептов, которые зависели от элементов, расположенных дальше по файлу рецептов. Теперь прокручивайте этот «неизвестный» список, пока (а) он не станет пустым; (б) у вас нет ничего со всеми известными ингредиентами. В случае (b) у вас что-то не так с вводом: либо ингредиент без определения, либо круговая зависимость. Распечатайте оставшийся список рецептов и отладьте входные файлы.

В случае (а) вы теперь готовы распечатать список Food.txt. Пройдитесь по своему «известному» списку и выпишите по одному предмету или рецепту за раз. Когда вы доберетесь до элемента [atomic_count], запишите второй файл, новый список рецептов. Это ваш старый список рецептов, но в полезном порядке сверху вниз. В дальнейшем у вас не будет "неизвестных" рецептов после первого прохождения.

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

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

person Prune    schedule 14.09.2015

Я все еще не совсем уверен, что вы спрашиваете, но это то, что я придумал...

from collections import OrderedDict

food_map = {'omelette': {'eggs': 5, 'price': None}, 'pileofomelettes': {'eggs': 25, 'price': None}, 'eggs': {'price': 5}}

with open('food.txt', 'rb') as f:
    data = f.read().splitlines()

data = OrderedDict([(x[0], int(x[1])) for x in [x.split(': ') for x in data]])

for key, val in data.items():
    if key == 'eggs':
        continue
    food_rel = food_map.get(key, {})
    val = food_rel.get('eggs', 1) * food_map.get('eggs', {}).get('price', 1)
    data[key] = val

with open('out.txt', 'wb') as f:
    data = '\n'.join(['{0}: {1}'.format(key, val) for key, val in data.items()])
    f.write(data)
person Cody Bouche    schedule 14.09.2015
comment
Да, я думаю, что функция OrderedDict будет очень полезной — вернет ли она что-то вроде {яйца: 5, омлет: 20, кучка омлетов: 120}? Потому что, если это так, то все, что я хочу сделать, это написать множество отдельных операторов, которые просто (отношения которых я буду жестко кодировать) обновляют значения, хранящиеся в словаре. - person Cubbs; 15.09.2015