Приглашаем посетить
Хлебников (hlebnikov.lit-info.ru)

1.1. Работа с подстроками

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

Введение


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

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

Фундаментальной единицей для работы с данными в Perl является скаляр (scalar), то есть отдельное значение, хранящееся в отдельной (скалярной) переменной. В скалярных переменных хранятся строки, числа и ссылки. Массивы и хэши представляют собой соответственно списки или ассоциативные массивы скаляров. Ссылки используются для косвенных обращений к другим величинам; они отчасти похожи на указатели в языках низкого уровня. Числа обычно хранятся в формате вещественных чисел с двойной точностью. Строки в Per! могут иметь произвольную длину (ограниченную' только объемом виртуальной Памяти вашего компьютера) и содержат произвольные данные - Даже двоичные последовательности с нулевыми байтами.

Строка не является массивом байт; к отдельному символу нельзя обратиться по индексу, как к элементу массива - для этого следует воспользоваться функцией substr. Строки, как и все типы данных Perl, увеличиваются и уменьшаются в размерах по мере необходимости. Неиспользуемые строки уничтожаются системой сборки мусора Perl (обычно при выходе переменной, содержащей строку, за пределы области действия или после вычисления выражения, в которое входит строка). Иначе говоря, об управлении памятью можно не беспокоиться - об этом уже позаботились до вас.

Скалярная величина может быть определенной или неопределенной. Определенная величина может содержать строку, число или ссылку. Единственным неопределенным значением является undef, все остальные значения считаются определенными - даже 0 и пустая строка. Однако определенность не следует путать с логической истиной; чтобы проверить, определена ли некоторая величина, следует воспользоваться функций defined. Логическая истина имеет особое значение, которое проверяется логическими операторами && и | |, а также в условии блока while.

Две определенные строки считаются ложными: пустая строка ("") и строка единичной длины, содержащая цифру "ноль" ("О"). Возможно, второе вас несколько удивит, по это связано с тем, что Perl выполняет преобразования между числами и строками по мере необходимости. Числа 0. 0.00 и 0.00000000 без кавычек считаются ложными значениями, но в строках они становятся истинными (так, строка "0. 00" считается истинной, а не ложной). Все остальные определенные значения (например, "false", 15 и \$х) истинны.

В строковом контексте значение undef интерпретируется как пустая строка (""). В числовом контексте undef интерпретируется как 0, а в ссылочном - как нуль-ссылка. При этом во всех случаях оно считается ложным. Использование неопределенной величины там, где Perl ожидает получить определенную, приводит к записи в STDERR предупреждения времени выполнения (если был использован флаг -w). Для простого вопроса о том, является ли нечто истинным или ложным, предупреждение не выдается. Некоторые операции не выдают предупреждений при использовании переменных, содержащих неопределенные значения. К их числу относятся операции автоматического увеличения и уменьшения, ++ и --, а также сложение и конкатенация с присваиванием, += и . =.

В программах строки записываются в апострофах или кавычках, в форме q// или qq// или "встроенных документов" (hero-documents). Апострофы используются в простейшей форме определения строк с минимальным количеством специальных символов: ' - завершает строку, \' - вставляет в нее апостроф, а \\ - обратную косую черту:

$string = '\n';      # Два символа, \ и n
$string = 'Jon \'Maddog\' Orwant';  # Внутренние апострофы

В строках, заключенных в кавычки, возможна интерполяция имен переменных (но не вызовов функций - о том, как это делается, см. рецепт 1.10). В них используется множество служебных символов: "\п" - символ перевода строки, "\033" - символ с восьмеричным кодом 33, "\cJ" - Ctrl+J и т. д. Полный список приведен в странице руководства рег1ор(\).

$string = "\n";                      # Символ перевода строки 
$string = "Jon \"Maddog\" Orwant";  # Внутренние кавычки

Операторы q// и qq// позволяют чередовать разделители строк с апострофами и кавычками. Например, строку с внутренними апострофами проще записать в следующем виде, вместо того чтобы использовать служебные символы '\'

$string = q/Jon 'Maddog' .Orwant/;   #Внутренние апострофы

В качестве разделителей могут использоваться одинаковые символы, как в этом примере, или парные (для различных типов скобок):

$string = q[Jon	'Maddog'Orwant]# Внутренниепострофы
$string=q{Jon'Maddog'O.rwant}# Внутренние апострофы 
$string= q(Jon	'Maddog'Orwant)#Внутренние апострофы 
$string=q<Jon'Maddog'Orwant> #Внутренние апострофы

Концепция "встроенных документов" позаимствована из командных интерпретаторов (shell) и позволяет определять строки, содержащие большое количество текста. Текст может интерпретироваться по правилам для строк, заключенных в апострофы или кавычки, и даже как перечень исполняемых команд - в зависимости от того, как задается завершающий идентификатор. Например, следующий встроенный документ будет интерпретироваться по правилам для строк, заключенных в кавычки:

$а = <<"EOF";
This is a multiline here document 
terminated by EOF on a line by itself 
EOF 

Обратите внимание: после завершающего EOF точка с запятой не ставится. Встроенные документы более подробно рассматриваются в рецепте 1.11.

Предупреждение для программистов из других стран: в настоящее время Perl не обладает прямой поддержкой многобайтовых кодировок (в версии 5.006 ожидается поддержка Unicode), поэтому в тексте книги понятия байт и символ считаются идентичными.

1.1. Работа с подстроками

Проблема

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

Решение


Функция substr предназначена для чтения и записи отдельных байтов строки:

$value = substr($stringi $of.fset, 4count);# Получить 5-байтовую строку.пропостить 3
$value = substr($string, $offset);# затем две 8-байтовые строки, затем все остальное

substr($string, $offset, $count) = $newstring;
substr($string, $ofrset)         = $newtail;

Функция unpack ограничивается доступом только для чтения, но при извлечении нескольких подстрок работает быстрее:

($leading, $s1, $s2. $trailing) =unpack ("A5 x3 A8 A8 A*",$data); 

# Делений на группы из пяти байт
@fivers = unpack("A5" х (length($string)/5,), $string);

# Дележ строки на отдельные символы   
@chars = unpack("A1" x (lenght($string),$string)

Комментарий

В отличие от многих языков, в которых строки представлены в виде массива байтов (или символов), в Perl они относятся к базовым типам данных. Это означает, что для работы с отдельными символами или подстроками применяется функция unpack или substr.

Второй аргумент функции substr (смещение) определяет начало интересующей вас подстроки; положительные значения отсчитываются от начала строки, а отрицательные - с конца. Если смещение равно 0, подстрока начинается с начала. Третий аргумент определяет длину подстроки.

$strfng ="This is what you have";
# +013458789345S7890   Прямое индексирование
#  198765432697654321- : Обратное индексирование
#  0 соответствует 10,20  и т.д
$first= substr($string, 0, 1); # "Т" 
$start = substr ($string , 5, 2); # "is"
$rest = substr($string,13)     # "you have" 
$last = substr($string, -1)    # "e" 
$end   = substr($string. -4) #have" 
$piece = substr($string,-8, 3); # "you"

Однако функция substr позволяет не только просматривать части строки, но и изменять их. Дело в том, что subst г относится к экзотической категории левосторонних функций, то есть таких, которым при вызове можно присвоить значение. К тому же семейству относятся функции vec, pos и keys (начиная с версии 5.004). При некоторой фантазии функции local и ту также можно рассматривать как левосторонние.

$string = "This is what you have";
print $string;
This is what you have
substr($string, 5, 2) = "wasn't"; # заменить "is" на "wasn't" 
This wasn't what you have
substr($string, -12) = "ondrous"; # "This wasn't wondrous
 This wasn't wondrous
 substr($string, 0, 1) = "";      # Удалить первый символ
his wasn't wondrous
substr($string, -10) = "";        # Удалить последние 10 символов
 his wasn'

Применяя оператор =~ в сочетании с операторами s///, m// или tr///, можно заставить их работать только с определенной частью строки:

# =~ применяется для поиска по шаблону 
if (substr($string, -10) =~ /pattern/) {
	print "Pattern matches in last 10 characters\n";
};

# подставить "at" вместо "is", ограничиваясь первыми пятью символами 
substr($string, 0, 5) =~ s/is/at/g;

Более того, подстроки даже можно поменять местами, используя с каждой стороны присваивания несколько вызовов substr:

# Поменять местами первый и последний символ строки 
$а = "make a hat";
(substr($a,0,1), substr($a,-1)) = (substr($a,-1), substr($a,0,1));
print $a;
take a ham

Хотя функция unpack не является левосторонней, она работает значительно быстрее subst г, особенно при одновременном извлечении нескольких величин. В отличие от subst г она lie поддерживает непосредственные смещения. Вместо этого символ "х" нижнего регистра с числом пропускает заданное количество байт в прямом направлении, а символ "X" верхнего регистра - в обратном направлении.

# Извлечение подстроки функцией unpack
$а = "То be or not to be";

$b = unpack("x6 A6", $a);   # Пропустить 6 символов, прочитать 6 символов
print $b;
or not

($b, $c) = unpack("x6 A2 x5 A2", $a); # Вперед 6 прочитать 2; назад 5, прочитать 2 
print "$b\n$c\n";
or            
be

Иногда строка "режется" на части в определенных позициях. Предположим, вам захотелось установить позиции разреза перед символами 8,14, 20, 26 и 30 - в каждом из перечисленных столбцов начинается новое поле. В принципе можно вычислить форматную строку unpack - "А7 А6 A6 А4 А*",, но программист на Perl по природе ленив и не желает попусту напрягаться. Пусть за него работает Perl. Воспользуйтесь приведенной ниже функцией cut2fmt

sub cut2fmt {
	my(positions) = @_;
	my $templace=1;
	my $lastpos=1;
	foreach $place(positions){
		$template .= "A" . ($place - $lastpos) . " ";
		$lastpos=$place;
	};
	$template .= "A*";
	return  $template;
}

$fmt = cut2fmt(8, 14, 20, 26, 30);
print "$fmt\n";
A7 A6 A6 A6 A4 A*

Возможности функции unpack выходят далеко за пределы обычной обработки текста. Она также обеспечивает преобразование между текстовыми и двоичными данными.

Смотри также

Описание функций unpack и substr в perlfunc(1); подпрограмма cut2fmt из рецепта 1.18. Применение unpack для двоичных данных демонстрируется в рецепте 8.18.


Назад
Вперед