December 3, 2020

Python Diff Tool

Как написать свой аналог Diff Tool?

В вычислительной технике diff — утилита сравнения файлов, выводящая разницу между двумя файлами. Эта программа выводит построчно изменения, сделанные в файле (для текстовых файлов) или изменения сделанные в папках. Также утилита diff была разработана в начале 1970-х годов для операционной системы Unix.


Теперь разберемся как можно написать свой аналог подобной программы и как можно ее модифицировать.

Писать программу будем на языке Python.

Во-первых, прежде чем приступать к написанию кода мы должны разобраться каким образом мы будем сравнивать файлы, в случае если они одинаковые. Очевидно, что сравнивать файлы тем же алгоритмом, который мы будем использовать для нахождения измененных данных, это ресурсозатратно и усложняет читаемость нашего кода. Чтобы избежать подобных трудностей мы будем сравнивать файлы, используя <<хэш функции>>.

Что такое хэш функции и с чем их едят?

Хэш-функции – это функции, предназначенные для «сжатия» произвольного сообщения или набора данных, в некоторую битовую комбинацию фиксированной длины. Хэш-функции имеют разнообразные применения при проведении статистических экспериментов, при тестировании логических устройств, при построении алгоритмов быстрого поиска и проверки целостности записей в базах данных.
Криптографической хеш-функцией называется всякая хеш-функция, являющаяся криптостойкой, то есть удовлетворяющая ряду требований специфичных для криптографических приложений.

Рассмотрим несколько отличительных свойств хэш функций

  1. Детерминированние
    Это означает, что независимо от того, сколько раз вы анализируете определенный вход через хэш-функцию, вы всегда получите тот же результат. Это важно, потому что если вы будете получать разные хэши каждый раз, будет невозможно отслеживать ввод.
  2. Быстрое вычисление
    Хэш-функция должна быть способна быстро возвращать хэш-вход. Если процесс не достаточно быстрый, система просто не будет эффективна.
  3. Небольшие изменения в вводимых данных изменяют хэш
    Даже если вы внесете небольшие изменения в исходные данные, изменения, которые будут отражены в хэше, будут огромными.

Разница буквально в двух символах, пробела и восклицательного знака, но уже совсем другой вид

В нашем случае мы будем использовать хэш функции для сравнения двух файлов. По причине того, что хэш функции файлов, которые отличаются даже на один байт, имеют уже совсем другой вид, нам идеально подходит этот метод. Вот пример, как выглядит текст захэшированный с помощью md5 хэш функции. Таких функций есть огромное количество и хоть md5 не является криптостойкой, для наших целей это будет отличный выбор.


import hashlib            #hashlib библиотека
file_hash1 = hashlib.md5()
file_hash2 = hashlib.md5()
#Мы используем модуль hashlib и метод md5, чтобы превратить наши переменные
#в обьекты типа md5 _hashlib.HASH
with open(values[0] , 'rb') as file1:  #values[0] - путь к файлу
     data = file1.read()   
     file_hash1.update(data)
#Мы указываем путь к нашему файлу, открываем и записываем его содержимое в 
#переменную data, после чего используя метод update, мы обновляем наш хэш 
#обьект содержимым нашего файла
if file_hash1.hexdigest() == filehash2.hexdigest():
    print("Files are Equal")
#Для того, чтобы иметь возможность сравнить хэш функции наших двух файлов
#и превратить наши переменные в читаемый вид, используем метод hexdigest
#если наши хэши будут совпадать, значит вывести на экран, что файлы одинаковые

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

Мы не будем использовать уже готовые библиотеки, т.к программа получается неудобной и трудно воспринимается, особенно для пользователей, которые просто хотят, чтобы программа четко и понятно выполняла поставленную перед ней задачу. Все что нам понадобится, это стандартный синтаксис Python и никаких дополнительных библиотек.


Для начала разберемся, что же мы будем использовать.

В Python есть такой тип данных set() (множество), он и поможет нам в решение этой непростой задачи.

Множество в python - "контейнер", содержащий не повторяющиеся элементы в случайном порядке. Основное преимущество использования множества по сравнению со списком состоит в том, что он имеет более оптимизированный метод проверки того, содержится ли в множестве конкретный элемент. Это основано на структуре данных, известной как хэш-таблица, даже здесь нас преследует понятия хэш функций, таблиц и т.д Поскольку элементы в множестве неупорядочены, мы не можем получить доступ к элементам с помощью индексов, как мы делаем в списках. Хэш-таблицы напротив, массив, в котором хранятся указатели на записи, соответствующие заданным данным.
else:     #мы продолжаем наш предыдущий if-else блок кода
    diff = set(data1.decode("utf-8").split()).difference(data.decode("utf-8").split())
    diff1 = set(data.decode("utf-8").split()).difference(data1.decode("utf-8").split())
#Первым делом, записываем все в set()
#используем метод .decode("utf-8") т.к в предыдущем блоке кода, мы читали
#и записывали содержимое файла в байтах, хэш-функции работают только в байтах, "utf-8" - это стандартный формат кодирования символов
#дальше используем метод split() для разделения строки на отдельные слова/символы, которые разделены пробелом
#и наконец используем метод .difference() для того чтобы найти разницу между двумя множествами
set_A.difference(set_B) for (A - B)
set_B.difference(set_A) for (B - A)

Модифицируем нашу программу

Мы разобрали основную логику программы, теперь давайте разберемся, как можно ее модифицировать и сделать более удобной для конечного пользователя.

Мы разработаем простенькое, но удобное и понятное GUI - graphical user interface/графический интерфейс пользователя, для нашей программы. Для этого воспользуемся готовой библиотекой в Python, под названием PySimpleGUI.

PySimpleGUI не является стандартной библиотекой Python. Поэтому для ее установки, надо прописать в консоли, простую команду.

pip install PySimpleGUI

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

  1. Два поля для выбора пути к файлам, с помощью стандартного проводника.
  2. Кнопки для выполнения и завершения нашей программы.
  3. Поле для output'a, чтобы не выводить результат выполнения в консоль.

Обьяснять синтаксис не имеет смысла, поэтому все ссылки на документацию, в том числе и PySimpleGUI, оставлю в конце статьи.

Рассмотрим сам код

import PySimpleGUI as sg   #импортируем наш модуль как sg, для удобства
sg.theme('DarkGrey8')      #тема для нашего GUI

def gui_for_files():
    form = sg.FlexForm('Python diff tool v1.2')     #название нашей программы
    layout = [              
             [sg.Text('Please enter the path to your files.')], #Инструкция для пользователя
             [sg.Output(size=(60,15))],       #Окошко для output'a нашей программы
             [sg.In() ,sg.FileBrowse(file_types=(("Text Files", "*.txt"),))],  #sg.In() нужно для обозначения, что мы берем данные на ввод
             [sg.In() ,sg.FileBrowse(file_types=(("Text Files", "*.txt"),))],  #sg.FileBrowse, указываем допустимый тип файлов 
             [sg.Submit(), sg.Cancel()]       #Кнопки выполнения и завершения программы
             ]    
    global button, values    #обьявляем глобальные переменные, чтобы была возможность использовать их в других функциях
    button, values = form.Layout(layout).Read()
    #с помощью стандартного синтаксиса модуля PySimpleGUI присваиваем
    #переменной button значения наших кнопок для выполнения/завершения программы
    #переменной values значения входных данных (путь к нашим файлам) 
На выходе получим, что-то подобное

Как превратить наш скрипт, в полноценную программу?

Еще один шаг в улучшении нашей программы, это превращение нашего скрипта в исполняемый файл, с расширением .exe

Для чего нам это нужно? Согласитесь, конечному пользователю будет не очень удобно работать через консоль, к тому же не у всех предустановлен Python. Для устранения нашей проблемы, мы можем превратить наш скрипт в исполняемый файл, который будет работать вне зависимости от того, установлен ли Python на операционной системе нашего пользователя и которая к тому же будет запускаться всего лишь в два клика.

В достижении нашей цели, нам поможет замечательная библиотека PyInstaller

PyInstaller замораживает (упаковывает) приложения Python в автономные исполняемые файлы в Windows, GNU / Linux, Mac OS X, FreeBSD, Solaris и AIX.

Это не стандартная библиотека Python, поэтому нам придется ее установить с помощью простой команды

pip install pyinstaller

Дальше все не менее просто, переходим в директорию с нашим скриптом.py

И прописываем в консоли такую команду

pyinstaller --onefile pythonScriptName.py
#вместо pythonScriptName, указываем название нашего файла

Ждем пока выполнится команда, потом в папке с нашим файлом мы можем заметить, что появилось еще несколько папок и файлов.
Переходим в папку под названием dist

Там и будет находиться наша программа с расширением .exe

Наша программа теперь готова

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

ССЫЛКИ НА ВСЕ МАТЕРИАЛЫ

@f1r3f1ght3r