Rurecoder

Материал из ALT Linux Wiki

< Soft

Rurecoder
Разработчик(и) zvezdochiot
Первый выпуск ~2018
Лицензия PDM 1.0
Сайт github.com

Rurecoder - программа для дешифровки текста, на подобие Windows-программы Штирлиц.

Установка

$ git clone https://github.com/zvezdochiot/python-rurecoder #(based https://bitbucket.org/dkuryakin/recoder.git)
# cd python-rurecoder
# cp -r rurecoder /usr/lib64/python3/site-packages

Использование

Примечание: Чтобы расшифровать текст, скопируйте кракозябры в чистый текстовый файл и сохраните его в UTF-8

В консоли

$ echo "Îñíîâíàÿ Îëèìïèéñêàÿ äåðåâíÿ â" | python3 -m rurecoder
Основная Олимпийская деревня в

Декодировка файла с выводом

$ cat '/home/user/enca.txt'
Ëþäè â Ãðîóâëåíäå, ìàëåíüêîì (ïî ìåðêàì Êàëèôîðíèè) ãîðîäêå â øåñòüñîò æèòåëåé, âûõîäèëè íà óëèöû, ñòîÿëè ïåðåä ñâîèìè äîìàìè ñ öâåòàìè íà ïîäîêîííèêàõ è ñìîòðåëè, êàê ýòîò ïèðîêóìóëþñ âûðàñòàåò âûøå Ñüåððû-Íåâàäû. ß è ñàìà ñòîÿëà òàì â áëàãîãîâåíèè è óæàñå è ïîíèìàëà áåç âñÿêèõ ñëîâ, ÷òî åñëè íå ïîéäåò äîæäü, òî ñëåäóþùèå ïîæàðû áóäóò åù¸ óæàñíåå, à åñëè äîæäè âñ¸ æå ïîéäóò è îêàæóòñÿ ñëèøêîì îáèëüíûìè, òî ýòî ñîææ¸ííûå ãîðíûå ñêëîíû ñìîåò íàâîäíåíèÿìè. Âñ¸ áûëî áóêâàëüíî íà ãðàíè êàòàñòðîôû. Íî ðÿäîì áûëè öâåòû â ãîðøêàõ, è íåîáîææ¸ííûå ñîñíû, è òðóùèåñÿ î íîãè ñîáàêè, è ðåñòîðàí, îòêðûòûé äëÿ óæèíà; è ÷óâñòâîâàëîñü, ÷òî âñå íà óëèöå âçäûõàþò ñ áëàãîäàðíîñòüþ çà òî, ÷òî âñ¸ ýòî ó íèõ åù¸ åñòü. Õîòÿ áû íåíàäîëãî (Äèàíà Ìàðêóì, Äåñÿòûé îñòðîâ).
$ cat '/home/user/enca.txt' | python3 -m rurecoder
Люди в Гроувленде, маленьком (по меркам Калифорнии) городке в шестьсот жителей, выходили на улицы, стояли перед своими домами с цветами на подоконниках и смотрели, как этот пирокумулюс вырастает выше Сьерры-Невады. Я и сама стояла там в благоговении и ужасе и понимала без всяких слов, что если не пойдет дождь, то следующие пожары будут ещё ужаснее, а если дожди всё же пойдут и окажутся слишком обильными, то это сожжённые горные склоны смоет наводнениями. Всё было буквально на грани катастрофы. Но рядом были цветы в горшках, и необожжённые сосны, и трущиеся о ноги собаки, и ресторан, открытый для ужина; и чувствовалось, что все на улице вздыхают с благодарностью за то, что всё это у них ещё есть. Хотя бы ненадолго (Диана Маркум, Десятый остров).

Декодировка в новый файл

$ cat '/home/user/enca.txt' | python3 -m rurecoder > /home/user/decom.txt

GUI

Создайте файл с расширением .py со следующим содержимым и запустите его командой python3 file.py

import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import subprocess
import webbrowser

class TextWithUndo(scrolledtext.ScrolledText):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.undo_stack = []
        self.redo_stack = []

        # Горячие клавиши
        self.bind("<Control-z>", self.undo)
        self.bind("<Control-y>", self.redo)
        self.bind("<Control-Shift-Z>", self.redo)
        self.bind("<Key>", self.add_to_undo_stack)

        # Контекстное меню
        self.context_menu = tk.Menu(self, tearoff=0)
        self.context_menu.add_command(label="Вырезать (Ctrl+X)", command=self.cut_text)
        self.context_menu.add_command(label="Копировать (Ctrl+C)", command=self.copy_text)
        self.context_menu.add_command(label="Вставить (Ctrl+V)", command=self.paste_text)
        self.context_menu.add_command(label="Удалить (Del)", command=self.delete_text)
        self.context_menu.add_separator()
        self.context_menu.add_command(label="Выделить все (Ctrl+A)", command=self.select_all)
        self.context_menu.add_separator()
        self.context_menu.add_command(label="Отменить (Ctrl+Z)", command=self.undo)
        self.context_menu.add_command(label="Повторить (Ctrl+Y)", command=self.redo)

        self.bind("<Button-3>", self.show_context_menu)  # Правая кнопка мыши
        self.bind("<Button-1>", self.hide_context_menu)  # Левая кнопка мыши

    def add_to_undo_stack(self, event):
        if event.keysym not in ("Control_L", "Control_R", "Shift_L", "Shift_R",
                              "Alt_L", "Alt_R", "Z", "Y"):
            self.undo_stack.append(self.get("1.0", tk.END))
            self.redo_stack.clear()

    def undo(self, event=None):
        if len(self.undo_stack) > 0:
            current = self.get("1.0", tk.END)
            self.redo_stack.append(current)
            prev_state = self.undo_stack.pop()
            self.replace_text(prev_state)
        return "break"

    def redo(self, event=None):
        if len(self.redo_stack) > 0:
            current = self.get("1.0", tk.END)
            self.undo_stack.append(current)
            next_state = self.redo_stack.pop()
            self.replace_text(next_state)
        return "break"

    def replace_text(self, text):
        self.delete("1.0", tk.END)
        self.insert("1.0", text[:-1])  # Удаляем последний \n

    def show_context_menu(self, event):
        try:
            self.context_menu.tk_popup(event.x_root, event.y_root)
        finally:
            self.context_menu.grab_release()

    def hide_context_menu(self, event):
        self.context_menu.unpost()

    def cut_text(self):
        self.event_generate("<<Cut>>")

    def copy_text(self):
        self.event_generate("<<Copy>>")

    def paste_text(self):
        self.event_generate("<<Paste>>")

    def delete_text(self):
        self.event_generate("<Delete>")

    def select_all(self):
        self.tag_add(tk.SEL, "1.0", tk.END)
        self.mark_set(tk.INSERT, "1.0")
        self.see(tk.INSERT)
        return "break"

def process_paragraph(paragraph):
    try:
        result = subprocess.run(
            ['python3', '-m', 'rurecoder'],
            input=paragraph.encode('utf-8'),
            capture_output=True,
            check=True
        )
        return result.stdout.decode('utf-8')
    except subprocess.CalledProcessError as e:
        return f"Ошибка при обработке:\n{e.stderr.decode('utf-8')}"

def process_text():
    input_text = input_textbox.get("1.0", tk.END).strip()

    if not input_text:
        messagebox.showwarning("Пустой ввод", "Введите текст для обработки.")
        return

    paragraphs = [p.strip() for p in input_text.split('\n') if p.strip()]
    output_textbox.delete("1.0", tk.END)

    for idx, paragraph in enumerate(paragraphs, 1):
        output = process_paragraph(paragraph)

        if add_numbers_var.get():
            result_text = f"Абзац {idx}:\n{output}"
        else:
            result_text = output

        if remove_empty_lines_var.get():
            output_textbox.insert(tk.END, f"{result_text}\n")
        else:
            output_textbox.insert(tk.END, f"{result_text}\n\n")

    output_textbox.see(tk.END)

def handle_paste(event):
    try:
        selected = event.widget.tag_ranges(tk.SEL)
        if selected:
            event.widget.delete(tk.SEL_FIRST, tk.SEL_LAST)
        event.widget.insert(tk.INSERT, event.widget.clipboard_get())
        return "break"
    except tk.TclError:
        pass

def handle_cut(event):
    if event.widget.tag_ranges(tk.SEL):
        event.widget.clipboard_clear()
        event.widget.clipboard_append(event.widget.get(tk.SEL_FIRST, tk.SEL_LAST))
        event.widget.delete(tk.SEL_FIRST, tk.SEL_LAST)
        return "break"

def handle_delete(event):
    if event.widget.tag_ranges(tk.SEL):
        event.widget.delete(tk.SEL_FIRST, tk.SEL_LAST)
        return "break"

def handle_select_all(event):
    event.widget.tag_add(tk.SEL, "1.0", tk.END)
    event.widget.mark_set(tk.INSERT, "1.0")
    event.widget.see(tk.INSERT)
    return "break"

def open_github(event):
    webbrowser.open("https://github.com/Text-extend-tools/python-rurecoder")

# Создаем главное окно
root = tk.Tk()
root.title("Rurecoder GUI")
root.geometry("1000x600")

# Переменные для хранения состояний чекбоксов
add_numbers_var = tk.BooleanVar(value=True)
remove_empty_lines_var = tk.BooleanVar(value=False)

# Создаем вкладки
notebook = ttk.Notebook(root)
notebook.pack(fill=tk.BOTH, expand=True)

# Вкладка "Перекодировать текст"
tab_recoder = ttk.Frame(notebook)
notebook.add(tab_recoder, text="Перекодировать текст")

# Главный контейнер для вкладки перекодировки
main_container = ttk.Frame(tab_recoder)
main_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

# Фрейм для текстовых полей
text_frame = ttk.Frame(main_container)
text_frame.pack(fill=tk.BOTH, expand=True)

# Входное поле с предупреждением
input_frame = ttk.LabelFrame(text_frame, text="Ввод")
input_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)

# Добавляем предупреждение перед полем ввода
warning_label = ttk.Label(
    input_frame,
    text="Внимание! Каждая строка обрабатывается как отдельный абзац",
    foreground="red",
    font=("Arial", 10, "bold")
)
warning_label.pack(pady=(0, 5))

# Поле ввода с поддержкой undo/redo и контекстным меню
input_textbox = TextWithUndo(input_frame, wrap=tk.WORD, font=("Arial", 12))
input_textbox.pack(fill=tk.BOTH, expand=True)

# Выходное поле
output_frame = ttk.LabelFrame(text_frame, text="Результат")
output_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)

output_textbox = TextWithUndo(output_frame, wrap=tk.WORD, font=("Arial", 12), state=tk.NORMAL)
output_textbox.pack(fill=tk.BOTH, expand=True)

# Привязка обработчиков
input_textbox.bind("<<Paste>>", handle_paste)
input_textbox.bind("<Control-x>", handle_cut)
input_textbox.bind("<Delete>", handle_delete)
input_textbox.bind("<Control-a>", handle_select_all)

output_textbox.bind("<<Paste>>", handle_paste)
output_textbox.bind("<Control-x>", handle_cut)
output_textbox.bind("<Delete>", handle_delete)
output_textbox.bind("<Control-a>", handle_select_all)

# Фрейм для кнопки
button_frame = ttk.Frame(main_container)
button_frame.pack(fill=tk.X, padx=5, pady=5)

process_button = ttk.Button(button_frame, text="Распознать", command=process_text)
process_button.pack(fill=tk.X, padx=5, pady=5)

# Вкладка "Информация"
tab_info = ttk.Frame(notebook)
notebook.add(tab_info, text="Информация")

# Содержимое вкладки информации
info_frame = ttk.Frame(tab_info)
info_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

info_text = """Rurecoder GUI - написана Ахламовым Петром с помощью ChatGPT и DeepSeek. 2025 г.

Используется Rurecoder от zvezdochiot.

- Каждая строчка обрабатывается как отдельный абзац.
- В одном абзаце может быть только один вид кодировки.
"""

info_label = ttk.Label(
    info_frame,
    text=info_text,
    font=("Arial", 11),
    justify=tk.LEFT,
    padding=20
)
info_label.pack(fill=tk.BOTH, expand=True)

# Фрейм для чекбоксов
checkboxes_frame = ttk.Frame(info_frame)
checkboxes_frame.pack(pady=10)

# Чекбокс для подписи абзацев
add_numbers_check = ttk.Checkbutton(
    checkboxes_frame,
    text="Подписывать распознанные абзацы",
    variable=add_numbers_var
)
add_numbers_check.pack(anchor=tk.W)

# Чекбокс для удаления пустых строк
remove_empty_lines_check = ttk.Checkbutton(
    checkboxes_frame,
    text="Убрать пустую строку между абзацами",
    variable=remove_empty_lines_var
)
remove_empty_lines_check.pack(anchor=tk.W)

# Делаем ссылку кликабельной
link_label = ttk.Label(
    info_frame,
    text="Открыть GitHub проекта Rurecoder",
    foreground="blue",
    cursor="hand2",
    font=("Arial", 11, "underline")
)
link_label.pack(pady=(0, 20))
link_label.bind("<Button-1>", open_github)

# Обработчик клика по пустому месту в главном окне
def hide_menus(event):
    input_textbox.context_menu.unpost()
    output_textbox.context_menu.unpost()

root.bind("<Button-1>", hide_menus)

root.mainloop()


Rurecoder-gui.png

Дополнительно

Примеры кракозябр для теста

Исходная кодировка При декодировании воспринято как Результат Распознает
При воспроизведении применяется та же кодировка, что и при создании текста. Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства.
Windows-1251 Windows-1252 или
ISO 8859-1
Øèðîêàÿ ýëåêòðèôèêàöèÿ þæíûõ ãóáåðíèé äàñò ìîùíûé òîë÷îê ïîäú¸ìó ñåëüñêîãî õîçÿéñòâà.
KOI8-R ьХПНЙЮЪ ЩКЕЙРПХТХЙЮЖХЪ ЧФМШУ ЦСАЕПМХИ ДЮЯР ЛНЫМШИ РНКВНЙ ОНДЗ╦ЛС ЯЕКЭЯЙНЦН УНГЪИЯРБЮ.
ISO 8859-5 иш№юърџ §ыхъђ№шєшърішџ ўцэћѕ уѓсх№эшщ фрёђ ьюљэћщ ђюыїюъ яюфњИьѓ ёхыќёъюую ѕючџщёђтр.
CP 866 ╪шЁюър ¤ыхъЄЁшЇшърЎш ■цэ√ї уєсхЁэшщ фрёЄ ью∙э√щ Єюыўюъ яюф·╕ьє ёхы№ёъюую їюч щёЄтр. Теряет "я"
КОИ-8|KOI8-R Windows-1252 или
ISO 8859-1
ûÉÒÏËÁÑ ÜÌÅËÔÒÉÆÉËÁÃÉÑ ÀÖÎÙÈ ÇÕÂÅÒÎÉÊ ÄÁÓÔ ÍÏÝÎÙÊ ÔÏÌÞÏË ÐÏÄߣÍÕ ÓÅÌØÓËÏÇÏ ÈÏÚÑÊÓÔ×Á.
Windows-1251 ыЙТПЛБС ЬМЕЛФТЙЖЙЛБГЙС АЦОЩИ ЗХВЕТОЙК ДБУФ НПЭОЩК ФПМЮПЛ РПДЯЈНХ УЕМШУЛПЗП ИПЪСКУФЧБ.
ISO 8859-5 ћЩвЯЫСб мЬХЫдвЩЦЩЫСУЩб РжЮйШ ЧеТХвЮЩЪ ФСгд ЭЯнЮйЪ дЯЬоЯЫ аЯФпЃЭе гХЬигЫЯЧЯ ШЯкбЪгдзС.
CP 866 √╔╥╧╦┴╤ ▄╠┼╦╘╥╔╞╔╦┴├╔╤ └╓╬┘╚ ╟╒┬┼╥╬╔╩ ─┴╙╘ ═╧▌╬┘╩ ╘╧╠▐╧╦ ╨╧─▀г═╒ ╙┼╠╪╙╦╧╟╧ ╚╧┌╤╩╙╘╫┴.
7 бит {IROKAQ \LEKTRIFIKACIQ @VNYH GUBERNIJ DAST MO]NYJ TOL^OK POD_#MU SELXSKOGO HOZQJSTWA. не распознает
ISO 8859-5 Windows-1252 или
ISO 8859-1
ÈØàÞÚÐï íÛÕÚâàØäØÚÐæØï îÖÝëå ÓãÑÕàÝØÙ ÔÐáâ ÜÞéÝëÙ âÞÛçÞÚ ßÞÔêñÜã áÕÛìáÚÞÓÞ åÞ×ïÙáâÒÐ.
Windows-1251 ИШаЮЪРп нЫХЪваШдШЪРжШп оЦЭле УгСХаЭШЩ ФРбв ЬЮйЭлЩ вЮЫзЮЪ ЯЮФксЬг бХЫмбЪЮУЮ еЮЧпЩбвТР.
КОИ-8/KOI8-R хьЮчзпО МшузБЮьДьзпФьО НжщКЕ сЦяуЮщьы тпАБ эчИщКы БчшГчз ъчтЙЯэЦ АушЛАзчсч ЕчвОыАБрп.
Альтернативная кодировка/CP 866 ╚╪р▐┌╨я э█╒┌тр╪ф╪┌╨ц╪я ю╓▌ых ╙у╤╒р▌╪┘ ╘╨ст ▄▐щ▌ы┘ т▐█ч▐┌ ▀▐╘ъё▄у с╒█ьс┌▐╙▐ х▐╫я┘ст╥╨.
Альтернативная кодировка|CP 866 Windows-1252 ˜¨à®ª ï í«¥ªâà¨ä¨ª æ¨ï ëå £ã¡¥à­¨© ¤ áâ ¬®é­ë© ⮫箪 ¯®¤êñ¬ã ᥫì᪮£® å®§ï©á⢠. Не распознал ш, а, .
Windows-1251 �Ёа®Є п н«ҐЄваЁдЁЄ жЁп о¦­ле ЈгЎҐа­Ё© ¤ бв ¬®й­л© в®«з®Є Ї®¤кс¬г ᥫмбЄ®Ј® е®§п©бвў . Не распознал ш, а
КОИ-8 ≤╗Ю╝╙═О М╚╔╙БЮ╗Д╗╙═Ф╗О Н╕╜КЕ ёЦ║╔Ю╜╗╘ ╓═АБ ╛╝И╜К╘ Б╝╚Г╝╙ ╞╝╓ЙЯ╛Ц А╔╚ЛА╙╝ё╝ Е╝╖О╘АБ╒═.
ISO 8859-5 �ЈрЎЊ я эЋЅЊтрЈфЈЊ цЈя юІ­ых ЃуЁЅр­ЈЉ Є ст ЌЎщ­ыЉ тЎЋчЎЊ ЏЎЄъёЌу сЅЋьсЊЎЃЎ хЎЇяЉстЂ . Не распознал ш, а, .
CP437 ÿ¿α«¬á∩ φ½Ñ¬Γα¿Σ¿¬áµ¿∩ δσ úπíÑα¡¿⌐ ñáßΓ ¼«Θ¡δ⌐ Γ«½τ«¬ »«ñΩ±¼π ßѽ∞߬«ú« σ«º∩⌐ßΓóá.
UTF-8 Windows-1252 Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства. Не распознал а, э, с
Windows-1251 Широкая электрификация южных губерний даст мощный толчок подъёму сельского хозяйства.
КОИ-8 п╗п╦я─п╬п╨п╟я▐ я█п╩п╣п╨я┌я─п╦я└п╦п╨п╟я├п╦я▐ я▌п╤п╫я▀я┘ пЁя┐п╠п╣я─п╫п╦п╧ п╢п╟я│я┌ п╪п╬я┴п╫я▀п╧ я┌п╬п╩я┤п╬п╨ п©п╬п╢я┼я▒п╪я┐ я│п╣п╩я▄я│п╨п╬пЁп╬ я┘п╬п╥я▐п╧я│я┌п╡п╟.
ISO 8859-5 аЈаИб�аОаКаАб� б�аЛаЕаКб�б�аИб�аИаКаАб�аИб� б�аЖаНб�б� аГб�аБаЕб�аНаИаЙ аДаАб�б� аМаОб�аНб�аЙ б�аОаЛб�аОаК аПаОаДб�б�аМб� б�аЕаЛб�б�аКаОаГаО б�аОаЗб�аЙб�б�аВаА. Криво распознал
CP 866 ╨и╨╕╤А╨╛╨║╨░╤П ╤Н╨╗╨╡╨║╤В╤А╨╕╤Д╨╕╨║╨░╤Ж╨╕╤П ╤О╨╢╨╜╤Л╤Е ╨│╤Г╨▒╨╡╤А╨╜╨╕╨╣ ╨┤╨░╤Б╤В ╨╝╨╛╤Й╨╜╤Л╨╣ ╤В╨╛╨╗╤З╨╛╨║ ╨┐╨╛╨┤╤К╤С╨╝╤Г ╤Б╨╡╨╗╤М╤Б╨║╨╛╨│╨╛ ╤Е╨╛╨╖╤П╨╣╤Б╤В╨▓╨░.
UTF-16 CP 866 (♦8♦@♦>♦:♦0♦O♦ M♦;♦5♦:♦B♦@♦8♦D♦8♦:♦0♦F♦8♦O♦ N♦6♦=♦K♦E♦ 3♦C♦1♦5♦@♦=♦8♦9♦ 4♦0♦A♦B♦ <♦>♦I♦=♦K♦9♦ B♦>♦;♦G♦>♦:♦  ?♦>♦4♦J♦Q♦<♦C♦ A♦5♦;♦L♦A♦:♦>♦3♦>♦ E♦>♦7♦O♦9♦A♦B♦2♦0♦. Криво распознал