Найти тему
Блокнот математика

Рубрика "Секреты Вим". Преобразования

Привет, коллеги. Часто бывает нужно преобразовать число или что-то ещё в другую форму. В программировании это часто называют форматированным выводом. Есть более или менее стандартная функция printf и ее варианты: ей дается строка с "полями" определенного вида, и некие данные; и данные подставляются в поля, преобразуясь согласно указаниям.

В Вим тоже такая функция есть. Ее можно использовать для конвертации шестнадцатеричных чисел в десятичные и обратно, для округления чисел, для форматирования (добивание нулями или пробелами) и всё в таком роде.

Функция без затей называется printf. Поля, как обычно, начинаются с процента. Вот некоторые часто используемые:

%s - строка
%6S - строка, дополненная пробелами до 6 (не менее) символов
%6s- строка, дополненная пробелами до 6 (не менее) байт; это важно для многобайтных символов, включая кириллицу
%.9S - строка, обрезанная до 9 символов
%.9s - строка, обрезанная до 9 байт
%c - один байт (число обрезается до байта и выводится символ с этим номером)
%d - десятичное целое число
%5d - то же, но с пробелами слева до 5 символов (не менее)
%05d - то же, но с нулями слева до 5 символов (не менее)
%x - шестнадцатеричное число
%X - то же, но буквы заглавные (не 666dead, a 666DEAD)
%# X (без пробела) - добавить 0X перед числом
%o - восьмеричное число (с решеткой добавится нуль перед числом)
%f - вещественное число вида 42.666
%.3f - то же с указанием числа дробных знаков
%e и %E - число в экспоненциальной форме: 6.66e42 (разница в регистре символа e)
%g или %G - выбор формы числа за Вимом (%.3g выведет exp(3) как 20.086, а exp(30) - как 1.069e13)
%% - сам символ %.

Можно очень точно настраивать формат выводимых чисел: знак, ведущие нули и всё в таком роде.

Вот как это можно использовать. Например, попросту переводить числа:

:echo printf("%X", 666) (получим 29А)

Или можно вставить результат в текст: в режиме вставки

<C-R>=printf('0x%04x',666)<Enter>

Можно отформатировать таблицу чисел, чтобы все были ровные:

:%s/\d\+\.\d\+/\=printf("%.3f",str2float(submatch(0)))/g

Замена на всех строках ищет все числа вида "цифры точка цифры", и заменяет на результат выражения; выражение же есть функция, преобразующая совпадение в вещественное число и форматирующая его. Если цифр после точки меньше, число будет дополнено нулями; если больше - округлено.

Правда, с целой частью ничего не будет делаться. Ну, сконвертируйте всё в %e.

Помимо вычисляемого выражения замены, можно использовать функции. Например, так:

:let line1=1
:let line2=42
:for line in range(line2-line1)
:let L= split(getline(line+line1))
:call map(L,'printf("%.2f",str2float(v:val))')
:call setline(line+1,join(L))
:endfor

Здесь line2 - номер последней строки, которую вам надо обработать, а line1 - первой. getline извлечет строку из текста, split разделит ее на слова-числа, собрав и в список. map пробежит по списку, применяя к каждому элементу printf, предварительно сконвертировав его в число. join сложит числа в строку через пробел. setline вставит строку обратно в текст, заместив старую. Можно тело цикла записать и в одну строку - длинно, но красиво!

Для округлений есть специальная функция round(). Наряду с ceil() и floor(). Но printf тоже справляется с этой задачей.

А вот так можно выровнять по правому краю столбец имен (например):

:%s/\S\+/\=printf("%9S",submatch(0))/

Добавьте g, чтоб выровнять всю таблицу по правому краю. Потом можно легко выровнять по левому:

:%s/\(\s*\)\(\S\+\)/\2\1/g

Если имя длиннее девяти символов, оно не будет обрезано и будет "торчать". Если важно, чтоб таблица была ровной, пусть и ценой срезания последних символов, используйте %.9S

Число полей функции printf ограничено восемнадцатью.

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

Можно вычислять выражение в тексте. Например, так:

:imap === <ESC>daW:let @@=printf("%.5f",eval(@@))<CR>Pa

Курсор должен быть на выражении, в котором нет пробелов и которое отделено пробелами от текста слева и справа. Функция eval вычисляет выражение, printf контролирует длину дробной части. Безымянный регистр обозначается @@. Выражение будет удалено и на его место вставлено вычисленное значение.

Можно выделить выражение и вычислить его:

vmap === d:let @@=printf("%.5f",eval(@@))<CR>P

В обоих случаях знак = надо трижды ввести без пауз.

Имейте в виду, что вы выполняете что-то, что написано в тексте; если текст не ваш, это небезопасно. eval выполняет все выражения, не только арифметику.

Удачи, коллеги!

Научно-популярные каналы на Дзене: путеводитель
Новости популярной науки12 марта 2022