03-10-2008 15:52
![]() |
В этой главе рассматриваются: |
Простейший вид данных, который может потребоваться обработать, -- это неструктурированные данные. Это данные которые не имеют никакой внутренней структуры, установленной каким-либо особым образом. В некотором смысле, это наиболее сложный вид данных, с которым мы что-либо можем сделать (вообще с такими данными мало, что можно поделать).
Хорошим примером неструктурированных данных является обычный ASCII-файл, содержащий текст. В этой главе мы будем рассматривать некоторые вещи, которые можно сделать с такими файлами.
Текстовые файлы содержат данные, в читаемом человеком виде. Такой файл может быть создан
в текстовом редакторе, вроде vi или emacs в UNIX, Notepad в Windows, или
edit в DOS. Вы можете заметить, что файлы, созданные большинством из текстовых
процессоров не в формате ASCII-текста, а содержат некоторый проприетарный текстовый формат.
(Большинство текстовых процессоров имеют функции сохранения документа в формате ASCII-текста,
однако, при этом большая часть форматирования документа будет утеряна.) Кроме того, очень
может быть, что такие файлы с ASCII-текстом, были созданы некоторой компьютерной системой или
программой.
Файл с ASCII-текстом, как и все остальные файлы данных, ничто иное как последовательность байтов бинарных данных. Программное обеспечение, которым вы воспользуетесь для просмотра файла (например, редактор), будет всего лишь интерпретировать байты данных в ASCII-символы.
Самое простое, что мы можем сделать с ASCII-файлом -- это прочитать его в некоторую структуру данных, для дальнейших манипуляций. Наиболее подходящий формат для структуры данных зависит, конечно же, от сути данных в файле, а также того, что вы в дальнейшем планируете делать с этими данными. Тем не менее, для читаемого человеком текста, вероятнее всего, наиболее приемлимой структурой будет массив строк. Если вы заинтересуетесь отдельными словами в каждой отдельной строке, то возможно, имеет смысл, расщепить каждую строку на массив слов. Заметим, что при чтении текста, порядок следования слов является важным, именно поэтому в качестве структуры для хранения данных мы используем Perl-массив (который является упорядоченной структурой), вместо, скажем, хэша (который не является упорядоченным).
Напишем подпрограмму ввода, которая будет считывать неструктурированный текстовый файл в массив массивов.
Как и ранее, мы предполагаем, что файл поступает к нам через STDIN.
1: sub read_text {
2:
3: my @file;
4:
5: push @file, [split] while <STDIN>;
6:
7: return \@file;
8: }
Давайте рассмотрим код строку за строкой.
Строка 3 определяет переменную, которая будет содержать массив строк. Каждый элемент этого массива будет ссылкой на другой массив. Каждый элемент таких массивов второго уровня будет содержать одно слово строки.
Строка 5 выполняет основную часть работы. Разобраться с ее работой будет проще, если вы прочитаете ее в обратном порядке. В действительности это сокращение кода, который, будучи развернут, выглядит как нечто следующее:
while (<STDIN>) {
my @line = split(/\s+/, $_);
push @file, [@line];
}
что может быть несколько проще понять. Для каждой строки в файле, мы выполняем расщепление везде,
где бы нам не встретился пробельный символ. Затем создаем анонимный массив, который является копией
массива, возвращенного функцией split, и сохраняем ссылку, возвращенную конструктором анонимного
массива, в @file.
Помимо всего прочего, в этой строке кода, содержится наше неявное определение слова. В нашем случае,
мы используем встроенный символьный класс Perl-а \s, чтобы определить разделитель слов, в виде
пробельных символов (вспомните, что split использует разделитель \s+ по умолчанию). Ваше
реальное приложение может потребовать чего-либо более сложного.
Строка 7 возвращает ссылку на массив.
Наша новая функция может быть вызвана, например, так:
my $file = read_text;
теперь мы можем обратиться к любой строке файла так:
my $line = $file->[$x];
здесь $x содержит номер интересующей нас строки. После этого вызова, $line
будет содержать ссылку на массив строки. Другими словами, мы можем обратиться к любому
желаемому слову следующим образом:
my $word = $line->[$y];
или, если использовать исходную ссылку $file:
my $word = $file->[$x][$y];
Конечно, все это будет хорошо работать, если текстовый файл приемлимого размера, если же вы попытаетесь сохранить весь текст "Войны и мира" в памяти, то это может привести к тому, что компьютер начнет сбрасывать содержимое памяти на диск (прим.перев.: swapping memory to disk), что приведет к замедлению вашей программы. (Хотя, если у вас достаточно памяти, чтобы можно было сохранить весь текст "Войны и мира" в памяти без того, чтобы начался процесс замещения страниц памяти, то такой подход вполне может быть приемлимым решением).
Учитывая все сказанное выше, если вы планируете сохранять весь текс в памяти, то и здесь можно применить пару трюков. Если вам необходимо считать файл в массив строк, без расщепления строк на индивидуальные слова, то можно достичь этого одной строкой, вроде такой:
my @file = <FILE>;
Если, вам требуется, чтобы весь текст был сохранен в одной скалярной переменной,
то надо обратить внимание на переменную $/. Эта переменная является разделителем
записей, поступающих на устройство ввода, и ее значением по-умолчанию является символ
новой строки (прим.перев.: newline character). Это означает, что по-умолчанию, данные,
будут продолжаться считываться оператором <>, до тех пор, пока не встретится символ
новой строки. Присвоение этой переменной undef приведет к тому, что весь поток ввода
будет считан целиком. (Отметим, что $/, как и большинство внутренних переменных Perl,
является по-умолчанию глобальной, поэтому ее изменение в одном месте, окажет воздействие
на всю программу вцелом. По этой причине, хорошей практикой будет использование use local
и заключение в фигурные скобки, для того, чтобы убедиться, что все изменения имеют строго
ограниченную зону действия (прим.перев.: limited scope).) Другими словами, вы можете считать
целиком весь файл следующим образом:
local $/ = undef; my $file = <FILE>;
Можно установить $/ в любое значение, которое найдете более удобным для своих целей. Другим,
наиболее часто используемым, значением является пустая строка. Это переключает Perl в режим "абзац"
(прим.перев.: paragraph mode), когда пустая строка может использоваться в качестве разделителя
входных данных.
Если файл слишком велик, чтобы эффективно разместиться в памяти, то потребуется обрабатывать
не более одной строки за один раз (или записи за один раз, если вы изменили $/). Строчно-ориентированные
и запись-ориентированные данные будут рассмотрены в следующей главе, однако в оставшейся части
этой главы мы будем предполагать, что мы считывает весь файл в память за один раз.
После того, как мы считали файл в свои структуры данных, самым простейшим способом преобразования части данных, будет использование технологии регулярных выражений, которые обсуждались в предыдущей главе. В этом случае строки данных, или даже отдельные слова, становятся для нас неприемлимыми, -- наша жизнь станет намного легче если мы считаем весь файл целиком в скалярную переменную.
Пусть, мы имеем некий текстовый файл, в котором требуется заменить все упоминания о "Windows" на
"Linux", то мы можем написать короткий скрипт вроде такого:
my $file;
{
local $/ = undef;
$file = <STDIN>;
}
$file =~ s/Windows/Linux/g;
print $file;
Заметим, что секция, которая считывает данные, окружена в блок в фигурных скобках, чтобы обеспечить
ограниченную область видимости переменно $/. Кроме того, в команде замены, мы использовали
модификатор g, чтобы изменить все упоминания о Windows.
Заметим, что вся мощь замены с помощью регулярных выражений в нашем распоряжении. Это дает возможность, с минимумом усилий переписать нашу программу перевода, из предыдущей главы, с тем, чтобы перевести весь входной файл целиком, за одну операцию.
Одним, из числа полезных, действий, которое мы можем выполнить над текстовым файлом, является
сбор статистической информации. Довольно просто собрать информацию о количестве строк или слов в
файле. Немногим сложнее обнаружить наидлиннейшее слово или сформировать таблицу, в которой
подсчитываются упоминания каждого слова. В следующих примерах мы будем предполагать, что файле
считан, с использованием функции read_text, которая была определена ранее в данной главе. Эта
функция возвращает ссылку на массив массивов. Мы будем разрабатывать скрипт, который подсчитывает
строки и слова в файле, а затем готовит отчит о длинах слов и наиболее часто употребляемых словах
в тексте.
1: # Переменные, отслеживающие то место, где мы находимся в файле
2: my ($line, $word);
3:
4: # Переменные, хранящие статистику
5: my ($num_lines, $num_words);
6: my (lengths);
7:
8: my $text = read_text();
9:
10: $num_lines = scalar @;
11:
12: foreach $line (@) {
13: $num_words += scalar @;
14:
15: foreach $word (@) {
16: $words++;
17: $lengths{length $word}++;
18: }
19: }
20:
21: my @sorted_words = sort { $words <=> $words } keys %words;
22: my @sorted_lengths = sort { $lengths <=> $lengths } keys %lengths;
23:
24: print "Ваш файл содержит $num_lines строк ";
25: print "и $num_words слов\n\n";
26:
27: print "Пятерка наиболее популярных слов:\n";
28: print map { "$_ ($words раз)\n" } @sorted_words[0 .. 4];
29:
30: print "\nПятерка наиболее популярных, по длине, слов:\n";
31: print map { "$_ ($lengths words)\n" } @sorted_lengths[0 .. 4];
Строка 2 объявляет две переменные, которые мы будем использовать для отслеживания местоположения в файле.
Строки 5 и 6 объявляют четыре переменные, которые мы будем использовать для накопления статистики.
$num_lines и $num_words это количество строк и слов в файле. %words это хэш, который
будет хранить количество упоминаний каждого слова, упоминающегося в файле. Его ключем будет слово, а
значением количество упоминаний слова. %lengths это хэш, который хранит частоту появления слов,
с одинаковой длиной, и организован сходным образом.
Строка 8 вызывает нашу функцию read_text, чтобы получить содержимое файла.
Строка 10 вычисляет число строк в файле. Это всего лишь количество элементов в массиве $text.
Строкой 12 начинается цикл перебирающий строку за строкой в массиве.
Строка 13 увеличивает переменную $num_words, на значение, равное количеству элементов в массиве
$line. Это значение равно количеству слов в строке.
Строка 15 начинает цикл перебирающий слова в строке.
Строки 16 и 17 наращивают соответствующие элементы в двух хэшах.
Строки 21 и 22 создают два массива, которые содержат ключевые значения хэшей %words и %lengths,
отсортированные в порядке убывания соответствующих им хэш-значений.
Строки 24 и 25 печатают общее количество слов и строк в файле.
Строки 27 и 28 печатают пять наиболее популярных слов в файле, используя первые пять элементов
в массиве @sorted_words, печатая, кроме того, значения ассоциированные с таким ключем, в хэше
%words. Строки 30 и 31 выполняют ту же самую задачу для массива @sorted_lengths.
В качестве последнего примера, сбора текстовой статистики, давайте вычислим среднюю длину слова в файлах.
И снова, мы будем использовать, уже существующую функцию read_text, для того, чтобы считать наш текст.
my ($total_length, $num_words);
my $text = read_text();
my ($word, $line);
foreach $line (@) {
$num_words += scalar @;
foreach $word (@) {
$total_length += length $word;
}
}
printf "Средняя длина слова составляет %.2f\n", $total_length / $num_words;
Одной, из наиболее полезных вещей, которые вы можете пожелать сделать с неструктурированными данными, является задача простого форматного преобразования. В этом разделе рассматриваются три наиболее типичных преобразования, которые могут встретиться в вашей практике.
Большинство текстовых данных, с которыми вы будете иметь дело, будут в виде ASCII, однако вполне возможно, что вы столкнетесь и с другими кодировками (прим.перев.: character set). Если вы обменивались данными с системами, которые работают на, так называемых, IBM mainframe, то вам частенько приходилось конвертировать данные в/из EBCDIC. Кроме того, вы может быть столкнетесь с многобайтовыми символами, если вам придется иметь дело с данными, поступающими из стран, где такие символы являются обычным делом (такие как Китай или Япония).
Чтобы поддерживать многобайтовые символы, Perl версии 5.6, включает в себя некоторую поддержку Unicode,
посредством модуля utf8. Такое новшество в Perl имело своей целью упростить работу с XML (XML, для
определения всех своих символьных данных, использует Unicode формата UTF-8). Если вы имеете более старую
версию Perl, то вас могут заинтересовать модули Unicode::Map8 и Unicode::String.
Для преобразования между ASCII и EBCDIC вы можете воспользоваться модулем Convert::EBCDIC, который
можно найти на CPAN. Этот модуль может быть использован как в объектном, так и в традиционном варианте.
Как традиционный модуль, он экспортирует две функции -- ascii2ebcdic и ebcdic2ascii. Отметим, что
эти функции должны быть явно импортированы в ваше пространство имен. В объектном варианте, этот модуль
имеет два метода, с названиями toascii и toebcdic. Следующий пример использует традиционный метод,
для конвертирования ASCII-данных, поступающих на STDIN, в формат EBCDIC.
use strict;
use Convert::EBCDIC qw/ascii2ebcdic/;
my $data;
{
local $/ = undef;
$data = <STDIN>;
}
print ascii2ebcdic($data);
Второй пример использует объектный интерфейс для конвертирования EBCDIC-данных в ASCII.
use strict;
use Convert::EBCDIC;
my $data;
my $conv = Convert::EBCDIC->new;
my $data;
{
local $/ = undef;
$data = <STDIN>;
}
print $conv->toascii($data);
Конструктор Convert::EBCDIC получает один опциональный параметр, который является 256-символьной
строкой, которая определяет таблицу преобразования.
Как я уже говорил выше, ASCII-файл не более чем поток бинарных данных. И только программное обеспечение, которое мы используем для его обработки, может интерпретировать данные таким образом, чтобы получить строки текста. Одним из важнейших символов (или последовательности символов), в текстовом файле, является символ, который отделяет друг от друга различные строки текста. Когда, к примеру, текстовый редактор встречает такой символ в файле, он знает, что следующией символы должны быть отображены начиная с первой колонки, следующей строки, пользовательского дисплея.
С течением времени, два символа становятся наиболее часто используемым признаком окончания строки. Это символы с ASCII-кодами 10 (line feed -- перевод строки) и 13 (carriage return -- возврат каретки). Перевод строки используется в UNIX (и Linux) системах. В Apple Macintoshes используется возврат картеки. DOS и Windows используют комбинацию обоих этих символов, причем перевод строки следует за возвратом каретки.
Такое различие в признаках окончания строк не приводит к проблемам, когда файлы данных используются на той же системе,
где они создавались, однако когда вы начинаете передавать файлы данных между различными системами, это может привести
к недоразумениям. Вы можете редактировать файл, который был создан под Windows в UNIX-овском текстовом редакторе.
И если такое случится, то вы увидите дополнительные символы ^M в конце каждой строки текста. (На данный момент,
такое поведение редакторов становится довольно редким, так как большинство редакторов теперь отображают строки без ^M,
вместо этого отображая стиль окончания строки в своих статусных строках). Это печатный эквивалент символа возврата каретки,
который Windows вставляет перед переводом строки. Сходным образом, UNIX-овский текстовый файл, открытый в Windows Notepad
не будет иметь возврата картеки перед переводом строки, и таким образом, Notepad не распознает признак конца строки. Все строки
будут следовать одна за одной, сцепленные друг с другом черным прямоугольником, который является способом, которым Windows
предоставляет непечатный символ перевода строки.
Есть несколько способов избежать этой проблемы. Например, пересылка файлов меж системами, с использованием FTP в ASCII-режиме, при этом признаки окончания строк будут автоматически конвертироваться в соответствующую форму. И, в большинстве случаев, это гарантирует корректный результат, однако, бывают случаи, когда вам все равно встретятся файлы данных с некорректным, с точки зрения вашей системы, признаком окончания строк. И Perl, конечно же, превосходно подходит для решения этой проблемы.
Следующая программа может использоваться как фильтр, чтобы исправлять проблемные файлы. Она использует два параметра, которыми являются
признаки конца строки исходной и целевой систем. Это строки CR, LF или CRLF.
В этой программе, вместо использования \n и \r мы используем управляющие последовательности ASCII \cM и \cJ
(Ctrl-M и Ctrl-J). Это сделано по той причине, что Perl в данном случае куда умнее, чем мы можем предположить.
Когда Perl встречает последовательность \n в прогамме, он конвертирует ее в корректный, с точки зрения текущей системы, признак окончания строки.
В большинстве случаев, это весьма полезно (например, это значит, что вам не требуется писать print "some text\r\n";, когда вы используете
Perl под управлением Windows). Однако в данной ситуации, такое поведение Perl, будет мешать решению нашей проблемы, поэтому мы используем низкоуровневое
представление символов.
#!/usr/local/bin/perl -w
use strict;
(@ARGV == 2) or die "Error: не указаны исходный и/или целевой форматы.";
my ($src, $tgt) = @ARGV;
my %conv = (CR => "\cM",
LF => "\cJ",
CRLF => "\cM\cJ");
$src = $conv;
$tgt = $conv;
$/ = $src;
while (<STDIN>) {
s/$src/$tgt/go;
print;
}
Заметим, что в операторе замены мы используем модификатор o, так как мы знаем, что исходное значение $src не изменится в течение
исполнения while-цикла.
Иногда, поступающие к вам, неструктурированные данные будут содержать численные данные и все, что вам потребуется, это переформатировать числа к некоторому стандартному виду. Такой процесс распадается на два процесса. Сначала вы должны распознать заинтересовавшие вас числа, а затем потребуется переформатировать их.
Каким образом распознать число? Ответ зависит от того, с какого рода числами вы имеете дело. Это целые числа или с плавающей точкой? Могут ли они быть отрицательными? Могут ли они быть в экспоненциальной нотации (такие как, например, 1E6 для 1 * 10^6)? Когда вы ответите на эти вопросы, то сможете построить регулярное выражение, которое будет совпадать с конкретным типом числа, подлежащему обработке.
Чтобы найти совпадение с натуральными числами (положительными целыми), вы можете воспользоваться простым регулярным выражением вроде такого:
/\d+/
Чтобы найти совпадение с целыми числами (с опциональными знаками +/-), используйте
/[-+]?\d+/
Чтобы найти свопадение с числами с плавающей точкой, используйте
/[-+]?(\d+(\.\d*)?|\.\d+)/
Чтобы найти совпадение с числами, которые опционально могут быть представлены в экспоненциальной нотации, используйте
/[-+]?(?=\d|\.\d)\d*(\.\d*)?([eE]([-+]?\d+))?/
Так как шаблоны становятся все сложнее и сложнее, то вполне может настать время, когда имеет смысл рассмотреть возможность использования такой возможности Perl, как прекомпилированное регулярное выражение, и создать заранее подготовленное регулярное выражение, для вашего шаблона, находящего совпадение с числом. Вы можете сделать что-либо похожее на это:
my $num_re = qr/[-+]?(?=\d|\.\d)\d*(\.\d*)?([eE]([-+]?\d+))?/;
my @nums;
while ($data =~ /$num_re/g) {
push @nums, $1;
}
чтобы вывести список всех чисел в $data.
Если у вас есть функция reformat, которая изменяет число в желаемый вами формат, то можно воспользоваться кодом, вроде этого:
$data =~ s/$num_re/reformat($1)/ge;
который, помимо всего прочего, использует модификатор e, чтобы исполнить строку замещения, перед тем как воспользоваться ей.
sprintfПростейший способ преобразовать формат числа состоит в использовании функции sprintf. Такой подход позволит вам зафиксировать количество десятичных позиций; заполнить начальную часть числа пробелами или нулями; выровнять число к правой или левой части, внутри некоторого поля. Вот пример, некоторых действий, которые вы можете выполнить:
my $number = 123.456789;
my @fmts = ('0.2f', '.2f', '10.4f', '-10.4f');
foreach (@fmts) {
my $fmt = sprintf "%$_", $number;
print "$_: [$fmt]\n";
}
который выдаст следующий результат:
0.2f: [123.46] .2f: [123.46] 10.4f: [ 123.4568] -10.4f: [123.4568 ]
(Здесь используются квадратные скобки, чтобы в точности показать начало и конец каждого поля.)
Кроме того, имеется пара модулей, доступных на CPAN, которые позволят вам выполнить куда более изощренные преобразования чисел. Это Convert::SciEng и Number::Format.
Convert::SciEng -- это модуль, для преобразования чисел из/в формат, в котором они имеют постфиксную букву, указывающую амплитуду
(прим.перев.: magnitde) числа. Такое преобразование называется закреплением и откреплением числа (прим.перев.: fixing and unfixing the number). Модуль распознает две различные схемы закреплений (прим.перев.: schemes of fixes), -- SI и SPICE. Модуль обладает объектно-ориентированным интерфейсом. Новый объект создается вызовом метода класса new, и передачей ему строки, указывающей желаемую вами схему закрепления (SI или SPICE).
my $conv = Convert::SciEng->new('SI');
После этого можно начинать закреплять или откреплять числа. Например, такой код:
print $conv->unfix('2.34u');
напечатает значение 2.34e-06. Здесь "u" означает SI-символ микроединиц. Кроме того, вы можете передать в unfix массив, например так:
print map { "$_\n" } $conv->unfix(qw/1P 1T 1G 1M 1K 1 1m 1u 1p 1f 1a/);
в результате получим вот что:
1e+015 1000000000000 1000000000 1000000 1000 1 0.001 1e-006 1e-012 1e-015 1e-018
(помимо всего прочего этот пример демонстрирует полный диапазон постфиксов, определенных в схеме SI). Помимо этого, вы можете настроить формат, возвращаемых результатов, с использованием метода format, которому передается строка, специфицирующая формат. Форматная строка -- это просто строка, которая будет передана в sprintf, когда потребуется выдать значение. Значение форматной строки по-умолчанию %5.5g.
Конечно имеется и метод fix, который, в качестве аргумента, принимает число, и возвращает значение, с добавленной, корректной буквой постфикса:
print $conv->fix(100_000)
печатает "100K", а
print $conv->fix(1_000_000)
печатает "1M".
Модуль Number::Format -- это модуль более общего назначения, для форматирования чисел различными интересными способами. Как и Convert::SciEng, данный модуль имеет объектно-ориентированный интерфейс. Вызов метода new создает новый форматирующий объект. Этот метод получает, в качестве аргумента, хэш, который содержит различные опции форматирования. Эти опции детализированы в приложении А, вместе с другими объектными методами, содержащимися внутри Number::Format.
Вот некоторые примеры использования этого модуля:
my $fmt = Number::Format->new; # используем все по-умолчанию
my $number = 1234567.890;
print $fmt->round($number), "\n";
print $fmt->format_number($number), "\n";
print $fmt->format_negative($number), "\n";
print $fmt->format_picture($number, '###########'), "\n";
print $fmt->format_price($number), "\n";
print $fmt->format_bytes($number), "\n";
print $fmt->unformat_number('1,000,000.00'), "\n";
Результат будет таким:
1234567.89 1,234,567.89 -1234567.89 1234568 USD 1,234,567.89 1.18M 1000000
Слегка изменим опции форматирования:
my $fmt = Number::Format->new(INTL_CURRENCY_SYMBOL => 'GBP',
DECIMAL_DIGITS => 1);
my $number = 1234567.890;
print $fmt->round($number), "\n";
print $fmt->format_number($number), "\n";
print $fmt->format_negative($number), "\n";
print $fmt->format_picture($number, '###########'), "\n";
print $fmt->format_bytes($number), "\n";
print $fmt->unformat_number('1,000,000.00'), "\n";
и вот результат:
1234567.9 1,234,567.9 -1234567.89 1234568 GBP 1,234,567.89 1.18M 1000000
Если мы желаем форматировать числа в немецкой системе, то можем получить нечто похожее на это:
my $de = Number::Format->new(INT_CURR_SYMBOL => 'DEM ',
THOUSANDS_SEP => '.',
DECIMAL_POINT => ',');
my $number = 1234567.890;
print $de->format_number($number), "\n";
print $de->format_negative($number), "\n";
print $de->format_price($number), "\n";
что приведет к такому результату:
1.234.567,89 -1234567.89 DEM 1.234.567,89
И наконец, если бы мы были бухгалтерами, то мы могли бы пожелать что-нибудь такое:
my $fmt = Number::Format->new(NEG_FORMAT=> '(x)'); my $debt = -12345678.90; print $fmt->format_negative($debt);
и в результате получили следующее:
(12345678.90)
Конечно же, возможно комбинировать Number::Format с остальными способами, которые мы использовали ранее. Если бы мы имели текстовый документ,
содержащий числа в различных форматах, и желали бы убедиться, что они все в одном, некотором, нашем стандарте, то могли бы сделать нечто следующее:
use Number::Format;
my $data;
{
local $/ = undef;
$data = <STDIN>;
}
my $fmt = Number::Format->new;
my $num_re = qr/[-+]?(?=\d|\.\d)\d*(\.\d*)?([eE]([-+]?\d+))?/;
$data =~ s/$num_re/$fmt->format_number($1)/ge;
print $data;
Для тополнительной информации о переменных, управляющих вводом, таких как $/, смотри страницы руководства perldoc perlvar.
Для дополнительной информации о поддержке Unicode в Perl, смотри страницы руководства perldoc perlunicode и perldoc utf8.
Для дополнительной информации о sprintf, смотри страницы руководства perldoc -f sprintf.
Модули Convert::SciEng и Number::Format могут быть получены в CPAN. Когда они будут установлены, то их документация будет доступна
через использование команды perldoc.
<< '''Часть вторая. Обработка данных''' | Data Munging With Perl | Глава 6. Запись-ориентированные данные >>