Приглашаем посетить
Лесков (leskov.lit-info.ru)

1.12. Переформатирование абзацев

Назад
Глава 1 Строки
Вперед

1.12. Переформатирование абзацев

Проблема

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

Решение

Воспользуйтесь стандартным модулем Text::Wrap для расстановки разрывов строк в нужных местах:

use Text::Wrap;
@OUTPUT = wrap($LEADTAB, $NEXTTAB, @PARA);

Комментарий

В модуле Text::Wrap присутствует 4)ункция wrap (см. пример 1.3), которая получает список строк и переформатирует их в абзац с длиной строки не более $Text: :Wrap: : columns символов. Мы присваиваем переменной $columns значение 20; это гарантирует, что ни одна строка не будет длиннее 20 символов. Перед списком строк функции wrap передаются два аргумента: один определяет отступ первой строки абзаца, а второй - отступы всех последующих строк.

Пример 1.3. wrapdemo

#!/usr/bin/perl -w
# wrapdemo - демонстрация работы Text::Wrap
@input = ("Folding and splicing is the work of an editor,".. "not a mere collection of silicon", "and", "mobile electrons!"); use Text::Wrap qw($columns &wrap);
$columns = 20;
print "0123456789" x 2, "\n";
print wrap(" ", " ", @input), "\n":
Результат выглядит так:
01234567890123456789
Folding and splicing is the work of an editor, not a mere collection of silicon and mobile electrons!
В результате мы получаем один абзац, в которой каждая строка, кроме последней, завершается символом перевода строки: # Объединение нескольких строк с переносом текста use Text::Wrap;

undef $/;
print wrap('', '', split(/\s*\n\s*/, <>);
Если на вашем компьютере установлен модуль Term::ReadKey с СРАМ, вы можете воспользоваться им для определения размеров окна, чтобы длина строк соответствовала текущему размеру экрана. Если этого модуля нет, размер экрана иногда можно взять из $ENV{ COLUMNS} или определить по выходным данным команды stty. Следующая программа переформатирует и слишком короткие, и слишком длинные строки абзаца по аналогии с программой fmt. Для этого разделителем входных записей $/ назначается пустая строка (благодаря чему о читает целые абзацы), а разделителем выходных записей $\ - два перевода строки. Затем абзац преобразуется в одну длинную строку посредством замены всех символов перевода строки (вместе с окружающими пропусками) одиночными пробелами. Наконец, мы вызываем функцию wrap с пустыми отступами первой и всех последующих строк.

use Text::Wrap qw(&wrap $columns);
use Term::ReadKey qw(GetTerminalSize);
($columns) = GetTerminalSizeO;
($/, $\) = ('', "\n\n"); # Читать по абзацам, выводить два перевода строки while (о) { # Читать весь абзац s/\s*\n\s*/ /g; # Заменить промежуточные переводы строк пробелами print wrap('', '', $_); # и отформатировать }

Смотри также: Описание функций split и join Bperlfunc(1), страница руководства стандартного модуля Text::Wrap. Применение модуля Term::ReadKey с CPAN продемонстрировано в рецепте 15.6.

1.13. Служебные преобразования символов

Проблема

Некоторые символы выводимой строки (апострофы, запятые и т. д.) требуется преобразовать к специальному виду. Предположим, вы конструируете форматную строку для sprintf и хотите преобразовать символы % в %%.

Решение

Воспользуйтесь подстановкой, которая снабжает префиксом \ или удваивает каждый преобразуемый символ: # Обратная косая черта
$var =~ s/([charlist])/\\$1/g;
# Удвоение
$var =~ s/([charlist])/$1$1/g;

Комментарий

В приведенных выше решениях $var - модифицируемая переменная, a CHARLIST - список преобразуемых символов, который может включать служебные комбинации типа \t или \п. Если преобразуется всего один символ, можно обойтись без скобок:
$string =~ s/%/%%/g;
Преобразования, выполняемые в следующем примере, позволяют подготовить строку для передачи командному интерпретатору. На практике преобразование символов ' и " еще не сделает произвольную строку полностью безопасной для командного интерпретатора. Правильно собрать весь список символов так сложно, а риск так велик, что для запуска программ лучше воспользоваться списковыми формами system и ехес (см. рецепт 16.11) - в этом случае вы вообще избегаете взаимодействия с интерпретатором.
$string = q(mom said, "don't do that.");
$string =~ s/(['"])/\\$1/g;
Две обратные косые черты в секции заменителя были использованы потому, что эта секция интерпретируется по правилам для строк в кавычках. Следовательно, чтобы получить одну обратную косую черту, приходится писать две. Приведем аналогичный пример для VMS DCL, где дублируются все апострофы и кавычки: $string = q(mom said, "don't do that.");
$string =-- s/(['"])/$1$1/g;
С командными интерпретаторами Microsoft дело обстоит еще сложнее. В DOS и Windows COMMAND. СОМ работает с кавычками, но не с апострофами; не имеет представления о том, как поступать с обратными апострофами, а для превращения кавычек в литерал используется обратная косая черта. Почти все бесплатные или коммерческие Unix-подобные интерпретаторы для Windows пытаются исправить эту удручающую ситуацию. Кроме того, можно определить интервал с помощью символа -, а затем инвертировать его с помощью символа ". Следующая команда преобразует все символы, не входящие в интервал от А до Z: $string =~ s/(["a-z])/\\$1/g;
Для преобразования всех неалфавитных символов следует воспользоваться метасимволами \0 и \Е или функцией quotemeta. Например, следующие команды эквивалентны:
$string = "this \qis a test!\e";
$string = "this is\\ a\\ test!";
$string = "this " . quotemeta("is a test!");

Смотри также: Описание оператора s/// в perlre(1) \\perlop(1) описание функции quotemeta рассматривается в perlfunc{\). В рецепте 19.1 рассматривается преобразование служебных символов в HTML, а в рецепте 19.6 - о том, как обойтись без передачи интерпретатору строк со служебными символами.

1.14. Удаление пропусков в обоих концах строки

Проблема

В полученную строку могут входить начальные или конечные пропуски. Требуется удалить их.

Решение

Воспользуйтесь парой подстановок:

$string =~ s/"\s+//;
$string =~ s/\s+$//;
Также можно написать специальную функцию, которая возвращает нужное значение:
$string = trim($string);
@many = trim((a>many);
sub trim {
my @out = @)_;
for (@>out) {
s/-\s+//;
s/\s+$//:
}
return wantarray ? @out : $outLOJ;
}

Комментарий

У этой проблемы имеются различные решения, однако в большинстве случаев приведенный вариант является наиболее эффективным. Для удаления последнего символа из строки воспользуйтесь функцией chop. В версии 5 была добавлена функция chomp, которая удаляет последний символ в том и только в том случае, если он содержится в переменной $/ (по умолчанию - "\n"). Чаще всего она применяется для удаления завершающего символа перевода строки из введенного текста: " Вывести полученный текст заключенным в >
while() {
chomp;
print ">$_<\n";
}

Смотри также: Описание оператора s/// Qperlre(1) реrlор(1) описание функций chop и chomp Bperlfunc(1). Начальные пропуски удаляются в функции getnum из рецепта 2.1 и при разделении элементов списка в рецепте 4.1.


Назад
Вперед