Vim Quoted-Printable Decode

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

Экспорт книжки с самсунговского телефона возможен (правда, выбор способов ограничен, и отправить книжку по электронной почте, например, не получилось; к счастью, я могу передать файл через Bluetooth на рабочую машину). Но вот незадача — весь не-латинский текст закодирован в Quoted-Printable (см. раздел 6.7 RFC 2045).

По сути, кодируется последовательность байт, что позволяет кодировать и UTF-8, и UTF-32, просто будет два литерала (=FF=FF) или четыре литерала соответственно.

Кодирование это видимо вызвано тем, что телефонная книжка экспортирует контакты в формате vCard 2.1.

К своему разочарованию, я обнаружил, что подходящих декодеров нет. Идея-то была в том, чтобы натравить декодер либо на телефонную книжку целиком, либо воспользоваться командой в Vim/Neovim [range]g/regex/command, то есть выполнить команду command на тех строках, в которых встречается регулярное выражение regex.

Писать на Rust или Go такую утилиту я поленился. Помогли китайцы.

Я бы сказал, что китайское присутствие чувствуется даже на GitHub, и оно часто полезно; к примеру, лаунчер Wox, которым я пользуюсь каждый день — китайского авторства. Автор проект забросил, но я собрал последнюю версию самостоятельно и пользуюсь с большим удовольствием уже давно. Он по крайней мере работает. Новый проект, взявший за основу код Wox, после установки падает сразу же, да и работает в принципе значительно хуже.

Так и тут: нашлось несколько строк, которые позволяют декодировать Quoted-Printable в соответствии со стандартом. Плагин к Notepad++ не в состоянии сделать даже этого (к примеру, CRLF должен быть представлен в виде закодированной последовательности, но авторы плагинов к Notepad++ стандарт не читали).

Пришлось чуть освежить в памяти С++:

#include <iostream>
#include <string>
#include "QuotedPrintable.hpp"

int main()
{
   for (std::string line; std::getline(std::cin, line); )
   {
      std::string qstring;
      for (int i = 0; i < line.length(); i++) {
         if (strchr("=", line[i]) != NULL) {
            if (strchr("0123456789ABCDEF", line[i+1]) != NULL) {
               if (strchr("0123456789ABCDEF", line[i+2]) != NULL) {
                  qstring += line[i];
                  qstring += line[i+1];
                  qstring += line[i+2];
                  i += 2;
               }
               else { std::cout << line[i]; }
            }
            else { std::cout << line[i]; }
         }
         else {
            std::cout << QuotedPrintable::decode(qstring);
            qstring = "";
            std::cout << line[i];
         }
      }
      std::cout << QuotedPrintable::decode(qstring) << std::endl;
   }
}

Колхозно? О да. Работает? Более чем.

Программа считывает произвольное количество строк с STDIN и возвращает тот же текст, но с раскодированным Quoted-Printable. Командой %g/\v(%u003d[A-F0-9]{2}){2,}/.!qpdecode/ находим все строки, в которых есть Quoted-Printable, и передаём их по одной в qpdecode. На выходе получаем нормальный UTF-8.

多谢, xuzheyang!