Выборочное объединение двух или более файлов данных

StackOverflow https://stackoverflow.com/questions/1698188

Вопрос

У меня есть исполняемый файл, входные данные которого содержатся в файле ASCII формата:

$ GENERAL INPUTS
$ PARAM1 = 123.456
PARAM2=456,789,101112
PARAM3(1)=123,456,789
PARAM4       =
1234,5678,91011E2
PARAM5(1,2)='STRING','STRING2'
$ NEW INSTANCE
NEW(1)=.TRUE.
PAR1=123
[More data here]
$ NEW INSTANCE
NEW(2)=.TRUE.
[etcetera]

Другими словами, некоторые общие входные данные и некоторые значения параметров для ряда новых экземпляров.Объявление параметров нерегулярно;некоторые числа разделены запятыми, другие в экспоненциальной записи, третьи в кавычках, интервал непостоянный и т. д.

Оценка некоторых сценариев требует, чтобы я взял входные данные одного «основного» файла данных и скопировал данные параметров, скажем, экземпляров 2–6 в другой файл данных, который может уже содержать данные для указанных экземпляров (в этом случае данные должны быть перезаписаны) и, возможно, другие (данные, которые следует оставить без изменений).

Я написал лексер Flex и парсер Bison;вместе они могут съесть файл данных и сохранить параметры в памяти.Если я использую их для открытия обоих файлов (главного и «сценария»), не составит большого труда выборочно записать в третий, новый файл нужные параметры (как в "general input from 'scenario'; instances 1 though 5 from 'master'; instances 6 through 9 from 'scenario'; ..."), сохраните его и удалите исходный файл сценария.

Дополнительная информация:(1) файлы очень чувствительны, очень важно, чтобы пользователь был полностью защищен от изменения мастер-файла;(2) файлы имеют приемлемый размер (от 500 КБ до 10 МБ).

Я понял, что то, что я могу сделать в десяти строках кода, некоторые здесь могут сделать в двух.Как бы вы подошли к этой проблеме?Пифонический ответ заставил бы меня плакать.Серьезно.

Это было полезно?

Решение

Если вы уже можете разобрать этот формат (я бы попробовал это с помощью pyParsing, но если у вас уже есть работающее решение flexx/bison, это будет просто отлично), и проанализированные данные хорошо помещаются в памяти, тогда вы в основном там.Вы можете представить то, что вы читаете из каждого файла, в виде простого объекта с диктовкой для «общего ввода» и списком диктовок, по одному на каждый экземпляр (или, возможно, лучше, диктовкой экземпляров, где ключи являются номерами экземпляров, которые могут даст вам немного больше гибкости).Затем, как вы упомянули, вы просто выборочно «обновляете» (добавляете или перезаписываете) некоторые экземпляры, скопированные с мастера в сценарий, записываете новый файл сценария, заменяете им старый.

Чтобы использовать код flexx/bison с Python, у вас есть несколько вариантов: превратить его в DLL/so и получить к нему доступ с помощью ctypes или вызвать его из расширения, написанного на cython, оболочки SWIG, расширения Python C-API или SIP, Boost и т. д. и т. п.

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

  • (имя параметра, значение параметра)
  • ('$$$$', 'Общие входные данные')
  • ('$$$$', 'Новый экземпляр')

просто используя '$$$$' как своего рода произвольный маркер.Затем для объекта, представляющего все, что вы прочитали из файла, вы можете иметь:

import re

instidre = re.compile(r'NEW\((\d+)\)')

class Afile(object):

  def __init__(self, filename):
    self.filename = filename
    self.geninput = dict()
    self.instances = dict()

  def feed_data(self, listoftuples):
    it = iter(listoftuples)
    assert next(it) == ('$$$$', 'General Inputs')
    for name, value in it:
      if name == '$$$$': break
      self.geninput[name] = value
    else:  # no instances at all!
      return
    currinst = dict()
    for name, value in it:
      if name == '$$$$':
        self.finish_inst(currinst)
        currinst = dict()
        continue
      mo = instidre.match(name)
      if mo:
        assert value == '.TRUE.'
        name = '$$$INSTID$$$'
        value = mo.group(1)
      currinst[name] = value
    self.finish_inst(currinst)

  def finish_inst(self, adict):
    instid = dict.pop('$$$INSTID$$$')
    assert instid not in self.instances
    self.instances[instid] = adict

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

Слияние просто требует выполнения foo.instances[instid] = bar.instances[instid] для требуемых значений instid, где foo это Afile экземпляр файла сценария и bar это файл для основного файла, который будет перезаписан или добавлен по мере необходимости.

Я предполагаю, что для записи недавно измененного файла сценария вам не нужно повторять все особенности форматирования, которые могут иметь конкретные входные данные (если вы это сделаете, то эти особенности необходимо будет записать во время анализа вместе с именами и значениями). , поэтому просто зацикливаемся sorted(foo.instances) и выписываем каждое также в отсортированном порядке (после записи общего материала также в отсортированном порядке и с соответствующими $ this and that маркерных линий и при правильном переводе '$$$INSTID$$$' запись и т. д.) должно быть достаточно.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top