Доброго времени суток, друзья! Нашел себе занимательную задачку на ближайшее время, решил написать "звонилку" для Android. Приложение будет синхронизироваться с контактами в системе и выполнять определенные действия. При чем здесь quoted-printable, что это и зачем мне понадобилось — рассказываю в статье.


Итак, quoted-printable это система кодирования двоичного текста в текст, использующая печатаемые символы ASCII, и, судя по странице в английской версии википедии применяемая для кодирования/декодирования данных в сообщениях e-mail.


На самом деле это не совсем так. Есть такой формат файла — vCard. И именно в этом формате происходит импорт/экспорт контактов из любого смартфона с Android. Так вот, этот формат (имеющий расширение .vcf) в версии 2.1 также использует кодировку quoted-printable. Кириллица в этой кодировке имеет вид (пример): "=D0=9F=D1=80=D0=B8=D0=B2=D0=B5=D1=82", т.е. сначала каждый символ кириллицы кодируется в UTF-8 в последовательность из двух байтов, а затем каждый байт записывается в hex-представлении со знаком равно "=".


И вот в таком виде импортируются все контакты с кириллическими символами. Понятно что о чтении и редактировании файла речи не идет. А мне то как раз это и нужно. Попробовал через плагины в текстовых редакторах… Можно решить эту проблему, да, но уж слишком много манипуляций. Короче, пришлось засесть за написание декодера.


В процессе столкнулся с еще одной загвоздкой. Дело в том, что стандарт кодировки quoted-printable предусматривает строки максимальной длины в 75 символов, а потом делает переносы, дублируя символы "=". Понадобилась дополнительная функция для обьединения перенесенных строк.


Скрипт использует модуль quopri (у меня импортировался сразу, без установки).


import quopri
import os

List_contact = []
File = "Контакты.vcf"
with open (File) as file: # чтение файла с контактами
    for i in file:
        List_contact.append (i)

# функция для обьединения перенесенных строк
def Func (List_for_change):
    List_contact_1 = []
    for i in List_contact:
        if i[-2] == '=':
            List_contact_1.append (i[:-2])
        else:
            List_contact_1.append (i)
    with open ('File.txt', 'w') as file:
        for i in List_contact_1:
            file.write (i)
    List_contact_1 = []
    with open ('File.txt') as file:
        for i in file:
            List_contact_1.append (i)
    os.unlink ('File.txt') # удаление temp файла
    return (List_contact_1)

List_contact = Func (List_contact)

#Запись декодированного текста в файл
with open ('Contacts_Decode.txt', 'w') as file:
    for i in List_contact:
        Str_1 = bytes (i, 'UTF-8') # модуль quopri принимает на вход двоичные данные
        Str_2 = quopri.decodestring (Str_1)
        file.write (Str_2.decode ('UTF-8'))

Итог работы скрипта. Из строк вида:


BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;=D0=90=D0=BD=D0=B4=D1=80=D0=
=B5=D0=B9;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=D0=90=D0=BD=D0=B4=D1=80=D0=
=B5=D0=B9
TEL;CELL;PREF:80000000000
END:VCARD
BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;=D0=92=D0=B8=D0=BA=D1=82=D0=
=BE=D1=80 =D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8=D1=87;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=D0=92=D0=B8=D0=BA=D1=82=D0=
=BE=D1=80 =D0=9D=D0=B8=D0=BA=D0=BE=D0=BB=D0=B0=D0=B5=D0=B2=D0=B8=D1=87
TEL;CELL;PREF:80000000000
END:VCARD

Получаем:


BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;Андрей;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:Андрей
TEL;CELL;PREF:80000000000
END:VCARD
BEGIN:VCARD
VERSION:2.1
N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:;Виктор Николаевич;;;
FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:Виктор Николаевич
TEL;CELL;PREF:80000000000
END:VCARD

После редактирования файла при необходимости производим обратную кодировку:


List_contact_2 = []
with open('Contacts_Decode.txt') as file:
    for i in file:
        List_contact_2.append(i)
with open('Контакты_New.vcf', 'w') as file:
    for i in List_contact_2:
        Str_1 = bytes(i, 'UTF-8')
        Str_2 = quopri.encodestring(Str_1)
        Str_3 = Str_2.decode('UTF-8')
# quopri кодирует знак "=" в hex-значение "=3D", поэтому возвращаем его обратно
        Str_4 = Str_3.replace('=3D','=')
        file.write(Str_4)

На этом, по сути, можно было бы и закончить, но есть еще одна шутка штука. Показались мне строки, закодированые в quoted-printable странно похожими на некоторые url-адреса, которые каждый, пожалуй, встречал в адресной строке браузера, только вместо знака "=" со знаком "%". Вида (пример) "%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82". И что бы вы думали? Да-да. По всей видимости это тоже quoted-printable (надо будет поинтересоваться у html-мастеров). Все декодируется в кириллицу вышеописанным способом при условии замены "%" на "=".


Ах да. Совсем забыл. Если вдруг кому то понадобится, то китайские иероглифы и арабские буквы декодируются так же как и кириллические символы (лично проверил).


Ну вот и все, друзья, до свидания, авось и будет от трудов моих кому то маленькая польза.

Комментарии (1)


  1. Tishka17
    29.11.2019 10:39

    Не буду комментировать содержимое статьи. Но настоятельно рекомендую ознакомиться с PEP8. В частности, с правилами именования