Редактирование FB2

Решил продолжить начатую в другом разделе тему про редактирование опечаток в FB2-файлах (http://n8xx.com/post45136.html#p45136). В ходе обсуждения на Флибусте (http://flibusta.net/node/61500#comments) один из участников написал пару скриптов на Питоне и выложил их у себя в ЖЖ (http://morthan2006.livejournal.com/144216.html).

Работает следующим образом: с помощью первого скрипта правятся куски текста, которые потом вторым заменяются по всему тексту за один проход. Подробная инструкция: [spoiler]

Typo corrector
==============
Программа предназначена для исправления опечаток в файлах FB2. Работает
следующим образом:
1. Запускаем файл typocor.pyw.
2. Запускаем читалку файлов FB2 (я использую FBReader).
3. Находим опечатку. Копируем в буфер обмена фрагмент текста (в пределах одной
строки), содержащий опечатку. Чтобы быть уверенными, что скопированный
фрагмент уникален, копируйте не только саму опечатку, но и пару-тройку рядом
стоящих слов.
4. В окне typocor.pyw вставляем скопированный фрагмент из буфера в верхнее поле
ввода. Сразу же после вставки этот фрагмент появляется и в нижнем поле ввода,
куда тут же переключается фокус ввода.
5. Исправляем опечатку в нижнем поле. Если по каким-то причинам мы не хотим
исправлять опечатку, то нажимаем кнопку Cancel. Оба поля ввода очистятся и
программа будет готова к следующему циклу работы. Если же мы уверены в
правильности исправления, тогда нажимаем кнопку OK (или клавишу Enter).
6. Повторяем шаги с 3 по 5 столько раз, сколько нужно.
7. Закрываем окно программы typocor.pyw нажатием на крестик в правом верхнем
углу. В текущем каталоге появляется файл result.txt, содержащий пары строк
(исходная - исправленная).
8. Проверяем, что корректируемый файл FB2, result.txt и correct.py находятся в
одном каталоге. Запускаем correct.py с параметром, соответствующим имени
файла FB2. Пример:
correct.py Pelevin_Omon_Ra.fb2
Скрипт автоматически определяет кодировку файла FB2, считывает пары строк из
result.txt и заменяет старые строки на новые. Результат замены сохраняется в
файле, чьё имя соответствует имени исходного файла, но с окончание _corrected
(для случая, указанного в примере, файл результата будет называться
Pelevin_Omon_Ra_corrected.fb2).
9. Проверяем, что в откорректированном файле нет ошибок (мало ли, вдруг какая
накладка в процессе замены произошла!). Если ошибок нет, переименовываем его
в правильное имя и заменяем исходный файл.
Для работы программы требуется Python (тестировалось на версии 2.5) и библиотека
wxPython (тестировалось на версии 2.8). Работоспособность программы проверена
на Windows XP SP3 и Ubuntu 9.04.
Разрешаю переделывать и дорабатывать программу как угодно, при условии
использования её в некоммерческих целях.
С уважением, Morthan.[/spoiler]

Всё бы хорошо, но для работы первого скрипта
[spoiler]
#!/usr/bin/python
# -*- coding: utf-8 -*-

import wx

class MainFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds[\"style\"] = wx.CAPTION|wx.CLOSE_BOX|wx.MINIMIZE_BOX|wx.SYSTEM_MENU|wx.RESIZE_BORDER|wx.TAB_TRAVERSAL|wx.CLIP_CHILDREN
wx.Frame.__init__(self, *args, **kwds)
self.label_1 = wx.StaticText(self, -1, \"Original:\")
self.text_ctrl_1 = wx.TextCtrl(self, -1, \"\")
self.label_2 = wx.StaticText(self, -1, \"Corrected:\")
self.text_ctrl_2 = wx.TextCtrl(self, -1, \"\", style=wx.TE_PROCESS_ENTER)
self.btOK = wx.Button(self, wx.ID_OK, \"\")
self.btCancel = wx.Button(self, wx.ID_CANCEL, \"\")

self.__set_properties()
self.__do_layout()

self.Bind(wx.EVT_TEXT, self.onOriginalTextChange, self.text_ctrl_1)
self.Bind(wx.EVT_TEXT_ENTER, self.onCorrectedEnter, self.text_ctrl_2)
self.Bind(wx.EVT_BUTTON, self.btOKClick, self.btOK)
self.Bind(wx.EVT_BUTTON, self.btCancelClick, self.btCancel)

def __set_properties(self):
self.SetTitle(u\"Типа, корректор\")
self.SetBackgroundColour(wx.Colour(226, 226, 226))
self.text_ctrl_1.SetFocus()
self.btOK.SetDefault()

def __do_layout(self):
sizer_1 = wx.BoxSizer(wx.VERTICAL)
sizer_2 = wx.BoxSizer(wx.VERTICAL)
sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
sizer_2.Add(self.label_1, 0, wx.ALL, 2)
sizer_2.Add(self.text_ctrl_1, 0, wx.ALL|wx.EXPAND, 2)
sizer_2.Add(self.label_2, 0, wx.ALL, 2)
sizer_2.Add(self.text_ctrl_2, 0, wx.ALL|wx.EXPAND, 2)
sizer_3.Add(self.btOK, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5)
sizer_3.Add(self.btCancel, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 5)
sizer_2.Add(sizer_3, 1, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_CENTER_VERTICAL, 1)
sizer_1.Add(sizer_2, 1, wx.ALL|wx.EXPAND, 0)
self.SetSizer(sizer_1)
sizer_1.Fit(self)
self.Layout()
self.Centre()

def onOriginalTextChange(self, event):
self.text_ctrl_2.SetValue(self.text_ctrl_1.GetValue())
self.text_ctrl_2.SetFocus()
event.Skip()

def btOKClick(self, event):
if self.addStringPair():
self.clearFields()
event.Skip()

def btCancelClick(self, event):
self.clearFields()
event.Skip()

def onCorrectedEnter(self, event):
if self.addStringPair():
self.clearFields()
event.Skip()

def addStringPair(self):
# Если строки отличаются, то сохраняем их в списке...
s1 = self.text_ctrl_1.GetValue()
s2 = self.text_ctrl_2.GetValue()
if s1 != s2:
self.resLst.append((s1, s2))
return True
return False

def clearFields(self):
# Очищаем оба поля ввода и ставим фокус на первое...
self.text_ctrl_1.SetValue('')
self.text_ctrl_2.SetValue('')
self.text_ctrl_1.SetFocus()

class TypoCorrector(wx.App):
def OnInit(self):
wx.InitAllImageHandlers()
self.lst = []
mainframe = MainFrame(None, -1, \"\")
self.SetTopWindow(mainframe)
mainframe.resLst = self.lst
mainframe.Show()
return 1

def OnExit(self):
if len(self.lst) > 0:
f = open('result.txt', 'wt')
for x in self.lst:
f.write('\
'.join([x[0], x[1], '', '']).encode('utf-8'))
f.close()

if __name__ == '__main__':
typocor = TypoCorrector(0)
typocor.MainLoop()
[/spoiler]

требуется библиотека wxPython, с которой, насколько я понимаю, имеются некоторые проблемы. Если я понимаю неправильно, поправьте :). Если прав, есть ли другие варианты, кроме того, чтобы разобравшись в формате файла с парами по второму скрипту [spoiler]
#!/usr/bin/python
# -*- coding: utf-8 -*-

\"\"\"При запуске мы находим аргумент (файл FB2) и определяем его кодировку.\"\"\"

import re
import sys

def getCoding(filename):
\"\"\"Считывает файл FB2 и возвращает его кодировку\"\"\"
result = ''
f = open(filename, 'rt')
while True:
s = f.readline()
m = re.search('encoding=\"([^\"]+?)\"', s)
if m is not None:
result = m.group(1)
break
f.close()
return result

def getResult():
\"\"\"Считывает файл result.txt (в кодировке UTF-8). Возвращает список
кортежей (исходник-результат).\"\"\"
result = []
f = open('result.txt', 'rt')
s = f.read().decode('utf-8')
f.close()
a = ''
for x in s.split('\
'):
if x != '':
if a == '':
a = x
else:
result.append((a, x))
a = ''
return result

if __name__ == '__main__':
if len(sys.argv) < 2:
print 'Usage: correct.py filename.fb2'
print 'result.txt must be in current directory.'
else:
srcfile = sys.argv[1]
dstfile = srcfile.replace('.fb2', '_corrected.fb2')
coding = getCoding(srcfile)
etalon = getResult()
# Грузим файл в юникодную строку...
f = open(srcfile, 'rt')
src = f.read().decode(coding)
f.close()
# Поочерёдно заменяем чего попало...
for x, y in etalon:
src = src.replace(x, y)
# Сохраняем файл обратно...
f = open(dstfile, 'wt')
f.write(src.encode(coding))
f.close()
[/spoiler]строить его \"ручками\" в каком нибудь текстовом редакторе. Уточняю, речь идет о Maemo 5 (всё-таки не удержался и купил :)).
Всё это очень и очень неудобно. Лучше бы убедить или помочь автору читалки добавить функции простого редактирования на лету. Правда, я сам не люблю пинать авторов...
Уже написал в соответствующую группу в google groups... Ждем-с реакции. После AlReader'а, конечно, это - стоя и в гамаке... но хочется уже хоть как-то... Хотя есть и некоторый плюс в \"отдельной\" правке - AlReader довольно долго пересохраняет файл, а так этой процедуры можно избежать.