Глава 7 Доступ к файлам

Доступ к файлам

Введение

Файлы занимают центральное место в обработке данных. Как и во всем осталь­ном в Perl, простые операции с файлами выполняются просто, а сложные... как-нибудь да выполняются. Стандартные задачи (открытие файлов, чтение данных, запись данных) используют прос В этой главе рассматривается механика доступа к файлам: открытие файлов, передача сведений о том, с какими файлами вы собираетесь работать, блоки­ровка и т. д. Глава 8 «Содержимое файлов» посвящена работе с содержимым файлов: чтению, записи, перестановке Следующий фрагмент выводит все строки файла /usr/local/widgets/data, со­держащие слово "blue":
open (INPUT, "< /usr/local/widgets/data")
or die "Couldn't open /usr/local/widgets/data for reading: $!\n";
while (INPUT) {
print if /blue/, } close(INPUT);
Получение файлового манипулятора Доступ к файлам в Perl организуется при помощи файловых манипуляторов (filehandle) - таких, как INPUT из предыдущего примера. Манипулятор - это сим­волическое имя, которое представляет файл в операциях чтения/записи. Файло- вые манипуляторы не являются переменными. В их именах отсутствуют префик­сы $, @ или %, однако они наряду с функциями и переменными попадают в сим­вольную таблицу Perl. По этой причине не всегда удается сохранить файловый манипулятор в переменной или пере
$var = *STDIN;
my sub($var, *LOGFILE);
Файловые манипуляторы, сохраняемые в переменных подобным образом, не используются напрямую. Они называются косвенными файловыми манипулятора­ми (indirect filehandle), поскольку косвенно ссылаются на настоящие манипулято­ры. Два модуля, IO::File (стал стан Когда в наших примерах используются модули IO::File или IO::Handle, анало­гичные результаты можно получить с применением модуля FileHandle, посколь­ку сейчас он является интерфейсным модулем (wrapper). Ниже показано, как выглядит программа для поиска "blue" с применением мо­дуля IO::File в чисто объектной записи:
use 10::File:
$input = 10::File->new("< /usr/local/widgets/data")
or die "Couldn't open /usr/local/widgets/data for reading: $!\n":
while (defined($line = $input->getline())) {
chomp($line);
STDOUT->print($line) if $line =~ /blue/;
} $input->close();


Как видите, без прямого использования файловых манипуляторов программа читается намного легче. Кроме того, она гораздо быстрее работает. Но поделимся одним секретом: из этой программы можно выкинуть все стрел­ки и вызовы методов. В отличие от большинства объектов, объекты IO::File не обязательно использовать объектно-ориентированным способом. В сущности, они представляют собой анонимные фа Стандартные файловые манипуляторы Каждая программа при запуске получает три открытых глобальных файловых манипулятора: STDIN, STDOUT и STDERR. STDIN {стандартный ввод) явля­ется источником входных данных по умолчанию. В STDOUT {стандартный вы­вод) по умолчанию направляются выходные данные активных программах STDIN соответствует клавиатуре, a STDOUT и STDERR - экрану монитора:
while() { # Чтение из STDIN
unless (/\d/) {
warn "No digit found.\n"; # Вывод в STDERR
}
print "Read: ", $_; # Запись в STDOUT
} END { close(STDOUT) or die "couldn't close STDOUT: $!" }
Файловые манипуляторы существуют на уровне пакетов. Это позволяет двум пакетам иметь разные файловые манипуляторы с одинаковыми именами (по ана­логии с функциями и переменными). Функция open связывает 4)айловый манипу­лятор с файлом или программой, после Операционная система работает с файлами через файловые дескрипторы, зна­чение которых определяется функцией fileno. Для большинства файловых опера­ций хватает манипуляторов Perl, однако в рецепте 7.19 показано, как файловый дескриптор преобразуется в файл Операции ввода/вывода Основные функции для работы с файлами в Perl - open, print, <...> (чтение записи) и close. Они представляют собой интерфейсные функции для процедур буферизованной библиотеки ввода/вывода С stdio. Функции ввода/вывода Perl документированы в perlfunc( 1) и Важнейшей .функцией ввода/вывода является функция open. Она получает два аргумента - файловый манипулятор и строку с именем файла и режимом до­ступа. Например, открытие файла /tmp/log для записи и его связывание с мани­пулятором LOG FILE выполняется следу

open(LOGFILE, "> /tmp/log") or die "Can't write /tmp/log: $!";

Три основных режима доступа - < (чтение), > (запись) и » (добавление). До­полнительные сведения о функции open приведены в рецепте 7.1. При открытии файла или вызове практически любой системной функции* не­обходимо проверять возвращаемое значение. Не каждый вызов open заканчивает­ся успешно; не каждый файл удается прочитать; не каждый фрагмент данных, вы­водимый функцией print, достигает Системной функцией называется обращение к сервису операционной системы. Термин не имеет отно­шения к функции system в языках С и Perl.
syscall, возвращающих -1). Системное сообщение или код ошибки хранится в переменной $1 и часто используется в die или сообщениях warn. Для чтения записей в Perl применяется оператор <МАНИПУЛЯТОР>, также часто дублируемый функцией readllne. Обычно запись представляет собой одну стро­ку, однако разделитель записей можно изменить (см. главу 8). Если МАНИПУЛЯТОР не указывается, Perl открывае С абстрактной точки зрения файл представляет собой обычный поток байтов. Каждый файловый манипулятор ассоциируется с числом, определяющим теку­щую позицию внутри файла. Текущая позиция возвращается функцией tell и устанавливается функцией seek. В рецепте Когда надобность в файловом манипуляторе отпадает, закройте его функцией close. Функция получает один аргумент (файловый манипулятор) и возвращает true, если буфер был успешно очищен, а файл - закрыт, и false в противном слу­чае. Закрывать все манипулятор Неявное закрытие файлов реализовано для удобства, а не для повышения на­дежности, поскольку вы не узнаете, успешно ли завершилась системная функция. Не все попытки закрытия завершаются успешно. Даже если файл открыт только для чтения, вызов close может за
close(FH) or die "FH didn't close: $!"; Усердный программист даже проверяет результат вызова close для STDOUT в кон­це программы на случай, если выходные данные были перенаправлены в команд­ной строке, а выходная файловая система оказалась переполнена. Вообще-то об этом должна заботиться runtim Впрочем, проверка STDERR выглядит сомнительно. Даже если этот поток не за­кроется, как вы собираетесь на это реагировать? Манипулятор STDOUT по умолчанию используется для вывода данных функция­ми print, printf и write. Его можно заменить функцией select, которая полу­чает новый и возвращает предыдущий выходной манипулятор, используемый по умолчанию. Перед вызовом select долж
$old_fh = select(LOGFILE); # Переключить вывод на LOGFILE print "Countdown initiated ...\n";
select($old_fh); # Вернуться к выводу на прежний манипулятор
print "You have 30 seconds to reach minumum safety distance.\n";
Некоторые специальные переменные Perl изменяют поведение текущего фай­лового манипулятора вывода. Особенно важна переменная $ |, которая управляет буферизацией вывода для файловых манипуляторов. Буферизация рассматрива­ется в рецепте 7.12. Функции ввода/вывода в Perl делятся на буферизованные и небуферизованные (табл. 7.1). Несмотря на отдельные исключения, не следует чередовать их вызовы в программе. Связь между функциями, находящимися в одной строке таблицы, весьма условна. Например, но с Таблица 7.1 Функции ввода/вывода в Perl Действие Буферизованные функции Небуферизованные функции Открытие open,sysopen sysopen Закрытие close close Ввод <...>, readline sysread Вывод print syswrite Позиционирование seek, tell_ Позиционирование рассматривается в главе 8, однако мы также воспользуем­ся им в рецепте 7.10.

7.1. Открытие файла

Проблема

Известно имя файла. Требуется открыть его для чтения или записи в Perl.

Решение

Функция open отличается удобством, sysopen - точностью, а модуль IO::File позво­ляет работать с анонимным файловым манипулятором. Функция open получает два аргумента: открываемый файловый манипулятор и строку с именем файла и специальными символами, определяющими режим открытия:
open(SOURCE, "< $path")
or die "Couldn't open $path for reading: $!\n";
open(SINK, "> $path")
or die "Couldn't open $path for writing: $!\n";

где SOURCE - файловый манипулятор для ввода, a SINK - для вывода. Функции sysopen передаются три или четыре аргумента: файловый манипуля­тор, имя файла, режим и необязательный параметр, определяющий права досту­па. Режим представляет собой число, конструируемое из констант модуля Fcnti:
use Fcnti;
sysopen(SOURCE, $path, O.RDONLY)
or die "Couldn't open $path for reading: $!\n";
sysopen(SINK, $path, 0_WRONLY)
or die "Couldn't open $path for writing: $!\n";
Аргументы метода new модуля IO::File могут задаваться в стиле как open, так и sysopen. Метод возвращает анонимный файловый манипулятор. Кроме того, так­же возможно задание режима открытия в стиле fopen(3):
use 10::File; # По аналогии с
open $sink = 10::File->new("> $filename")
or die "Couldn't open $filename for writing: $!\n";
# По аналогии с sysopen
$fh = 10::File->new($filename, 0_WRONLY|0_CREAT)
or die "Couldn't open $filename for reading: $!\n";
# По аналогии с fopen(3) библиотеки
stdio $fh = 10::File->new($filename, "r+")
or die "Couldn't open $filename for read and write: $!\n";

Комментарий

Все операции ввода/вывода осуществляются через файловые манипуляторы независимо от того, упоминаются манипуляторы в программе или нет. Фай­ловые манипуляторы не всегда связаны с конкретными файлами - они также применяются для взаимодействия с другими прог Функция open позволяет быстро и удобно связать файловый манипулятор с файлом. Вместе с именем файла передаются сокращенные обозначения стандарт­ных режимов (чтение, запись, чтение/запись, присоединение). Функция не по­зволяет задать права доступа для созд Большинство программистов начинает работать с open задолго до первого ис­пользования sysopen. В таблице показано соответствие между режимами функ­ции open («Файл»), константами sysopen («Флаги») и строками fopen(3), переда­ваемыми 10: :File->new («Символы

7.1. Открытие файла 245

Файл

Чтение

Запись

Присое­

Созда­

Очистка

Флаги

Символы

 

 

 

 

 

 

динение

ние

содержи­

0

 

 

 

 

 

 

 

 

 

 

 

 

мого

 

 

 

 

• < файл

Да

Нет

Нет

Нет

Нет

RDONLY

"г"

> файл,

Нет

Да

Нет

Да

Да

WRONLY

"W"

режим

 

 

 

 

 

 

 

 

 

 

TRUNC

 

 

открытия>

 

 

 

 

 

 

 

 

 

 

CREAT

 

 

» файл>,

Нет

Да

Да

Да

Нет

WRONLY

"а"

режим

 

 

 

 

 

 

 

 

 

 

APPEND

 

 

открытня>

 

 

 

 

 

 

 

 

 

 

CREAT

 

 

+< файл

Да

Да

Нет

Нет

Нет

RDWR-

"г+"

+> файл,

Да

Да

Нет

Да

Да

RDWR

"W+"

режим

 

 

 

 

 

 

 

 

 

 

TRUNC

 

 

открытия>

 

 

 

 

 

 

 

 

 

 

CREAT

 

 

+» файл>,

Да

Да

Да

Да

Нет

RDWR

"а+"

режим

 

 

 

 

 

 

 

 

 

 

APPEND

 

 

открытия>

 

 

 

 

 

 

 

 

 

 

CREAT

 

 

 


Подсказка: режимы +> и +» почти никогда не используются. В первом случае файл уничтожается еще до того, как он будет прочитан, а во втором часто возни­кают затруднения, связанные с тем, что указатель чтения может находиться в произвольной позиции, но Функция sysopen получает три или четыре аргумента:
sysopen(FILEHANDLE, sysopen(FILEHANDLE,
$name, $flags) or die "Can't open $name : $! "; $name, $Hags, Sperms) or die "Can't open $name : $!"; Здесь $name - имя файла без «довесков» в виде < или +; $flags - число, полу­ченное объединением констант режимов 0_creat, 0_wronly, 0_trunc и т. д. операци­ей or. Конкретный состав доступных констант 0_ зависит от операционной си­стемы. Дополнительные све
0_RDONLY Только чтение.
0_WRONLY Только запись.
0_RDWR Чтение и запись.
0_CREAT Создание файла, если он не существует.
0_EXCL Неудачное завершение, если файл уже существует.
0_APPEND Присоединение к файлу.
0_TRUNC Очистка содержимого файла.
0_NONBLOCK Асинхронный доступ. К числу менее распространенных констант принадлежат 0_SHLOCK, 0_EXLOCK, 0_BINARY, 0_NOCTTY и 0_SYNC. Обращайтесь к странице руководства open (2) или к ее эквиваленту. Если функции sysopen не передается аргумент $perms, Perl использует восьме­ричное число 0666. Права доступа задаются в восьмеричной системе и учитывают текущее значение маски доступа (задаваемой функцией umask) процесса. В мас­ке доступа сброшенные биты с Если у вас возникнут затруднения с масками доступа, воспользуйтесь про­стым советом: передавайте значение 0666 для обычных файлов и 0777 для ка­талогов и исполняемых файлов. У пользователя появляется выбор: если ему понадобятся защищенные файлы, то может Приведем примеры практического использования open и sysopen. Открытие файла для чтения:

open(FH, "< $path") or die$!;
sysopen(FH, $path, 0_RDONLY) or die$!;

Открытие файла для записи (если файл не существует, он создается, а если су­ществует - усекается):
open(FH, "> $path") or die$!;
sysopen(FH, $path, 0_WRONLY|0_TRUNC|0_CREAT) or die$!;
sysopen(FH, $path, 0_WRONLY|0_TRUNC|0_CREAT, 0600) or die$!;

Открытие файла для записи с созданием нового файла (файл не должен суще­ствовать):
sysopen(FH, $path, 0_WRONLY|0_EXCL|0_CREAT) or die$!;
sysopen(FH, $path, 0_WRONLY|0_EXCL|0_CREAT, 0600) or die$!:

Открытие файла для присоединения (в случае необходимости файл создается):
open(FH, "» $path") or die$!; sysopen(FH, $path, 0_WRONLY|0_APPEND|0_CREAT) or die$!; sysopen(FH, $path, 0_WRONLY|0_APPEND|0_CREAT, 0600) or die$!;

Открытие файла для присоединения (файл должен существовать):
sysopen(FH, $path, 0_WRONLY|0_APPEND) or die$!;

Открытие файла для обновления (файл должен существовать):
open(FH, "+< $path") or die$!;
sysopen(FH, $path, 0_RDWR) or die$!;
Открытие файла для обновления (в случае необходимости файл создается):
sysopen(FH, $path, 0_RDWR|0_CREAT) or die$!;
sysopen(FH, $path, 0_RDWR|0_CREAT, 0600) or die$!;

Открытие файла для обновления (файл не должен существовать):
sysopen(FH, $path, 0_RDWR|0_EXCL|0_CREAT) or die$!;
sysopen(FH, $path, 0_RDWR|0_EXCL|0_CREAT, 0600) or die$!;

Маска 0600 всего лишь поясняет, как создаются файлы с ограниченным досту­пом. Обычно этот аргумент пропускается.

Смотри также --------------------------------
Описание функций open, sysopen и umask в perlfunc(1); документация по стан­дартным модулям IO::File и Fcnti; страницы руководства open(2), fopen(3) и umask(2); рецепт 7.2.

7.2. Открытие файлов с нестандартными именами

Проблема

Требуется открыть файл с нестандартным именем - например, "-"; начинающим­ся с символа <, > или |; содержащим начальные или конечные пропуски; закапчи­вающимся символом |. Функция open не должна принимать эти функции за слу­жебные, поскольку вам нужно сов

Решение

Выполните предварительное преобразование:
$filename =~ s#"(\s)#./$1#;
open(HANDLE, "< $filename\0") or die "cannot open $filename : $!\n":
Или просто воспользуйтесь функцией sysopen:
sysopen(HANDLE, $filename, 0_RDONLY) or die "cannot open $filename : $!\n";

Комментарий

Функция open определяет имя файла и режим открытия по одному строковому аргументу. Если имя файла начинается с символа, обозначающего один из режи­мов, open вполне может сделать что-нибудь неожиданное. Рассмотрим следующий фрагмент:
$filename = shift @ARGV;
open(INPUT, $filename) or die "cannot open $filename : $!\n":

Если пользователь указывает в командной строке файл ">/etc/passwd", програм­ма попытается открыть /etc/passwd для записи - со всеми вытекающими послед­ствиями! Режим можно задать и явно (например, для записи):
open(OUTPUT, ">$filename")
or die "Couldn't open $filename for writing: $!\n";
но даже в этом случае пользователь может ввести имя ">data", после чего програм­ма будет дописывать данные в конец файла data вместо того, чтобы стереть пре­жнее содержимое. Самое простое решение - воспользоваться функцией sysopen, у которой режим и имя файла передаются в разных аргументах:
use Fcnti; # Для файловых констант
sysopen(OUTPUT, $filename, 0_WRONLY|0_TRUNC)
or die "Couldn't open $filename for writing: $!\n";
А вот как добиться того же эффекта с функцией open для имен файлов, содер­жащих начальные или конечные пропуски:
$file =~ зГ(\з)#./$1#;
open(HANDLE, "> $file\0")
or die "Could't open $file for OUTPUT : $!\n";
Такая подстановка защищает исходные пропуски, но не в абсолютных именах типа " /etc/passwd", а лишь в относительных (" passwd"). Функция open не счита­ет нуль-байт ("\0") частью имени файла, но благодаря ему не игнорируются ко­нечные пропуски. Волшебная интерпретация файловых имен в функции open почти всегда оказы­вается удобной. Вам никогда не приходится обозначать ввод или вывод с помо­щью особой формы "-". Если написать фильтр и воспользоваться простой функ­цией open, пользователь сможет пер Вопросы безопасности open актуальны лишь для программ, работающих в осо­бых условиях. Если программа должна работать под управлением чего-то друго­го - например, сценариев CGI или со сменой идентификатора пользователя, - добросовестный программист всегда

> Смотри также -------------------------------
Описание 4)ункций open и sysopen в perlfunc(1); рецепты 7.1, 7.7, 16.2, 19.4 и 19.6.

7.3. Тильды в именах файлов

Проблема

Имя файла начинается с тильды (например, -usemame/blah), однако функция open не интерпретирует его как обозначение домашнего каталога (home directory).

Решение

Выполните ручное расширение с помощью следующей подстановки:
$filename =- s{ ~ ~ ( ["/]* ) } { $1
? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR}
| (getpwuid($>))[7] ) }ех;

Комментарий

Нас интересуют следующие применения тильды:
-user
-user/blah
-/blah

где user - имя пользователя. Если ~ не сопровождается никаким именем, используется домашний каталог те­кущего пользователя. В данной подстановке использован параметр /е, чтобы заменяющее выраже­ние интерпретировалось как программный код Perl. Если за тильдой указано имя пользователя, оно сохраняется в $1 и используется getpwnam для выбора домашнего каталога пользователя из воз

> Смотри также
Описание функции getpwnam в perlfunc(1); man-страница getpwnam(2) вашей системы; рецепт 9.6.

7.4. Имена файлов в сообщениях об ошибках

Проблема

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

Решение

Воспользуйтесь именем файла вместо манипулятора:
open($path, "< $path")
or die "Couldn't open $path for reading : $!\n";

Комментарий

Стандартное сообщение об ошибке выглядит так: Argument "3\n" isn't numeric in multiply at tallyweb line 16, chunk 17. Манипулятор LOG не несет полезной информации, поскольку вы не знаете, с каким файлом он был связан. Если файловый манипулятор косвенно передается через имя файла, предупреждения и сообщения об ошибках Perl становятся более содержательными: Argument "3\n" isn't numeric in multiply at tallyweb line 16, chunk 17. К сожалению, этот вариант не работает при включенной директиве strict refs, поскольку переменная $path в действительности содержит не файловый манипу­лятор, а всего лишь строку, которая иногда ведет себя как манипулятор. Фраг­мент (chunk), упоминаемый в п

> Смотри также -------------------------------
Описание функции open в perlfunc(1); рецепт 7.1.

7.5. Создание временных файлов

Проблема

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

Решение

Если имя файла не существенно, воспользуйтесь методом класса new_tmpfile модуля IO::File для получения файлового манипулятора, открытого для чтения и записи:
use 10::File;
$fh = 10::File->new_tmpfile
or die "Unable to make new temporary file: $!";
Если имя файла должно быть известно, получите его функцией tmpnam из мо­дуля POSIX и откройте файл самостоятельно:
use 10::File;
use POSIX qw(tmpnam);
# Пытаться получить временное имя файла до тех пор,
# пока не будет найдено несуществующее имя
do { $name = tmpnam() }
until $fh = 10::File->new($name, 0_ROWR|0_CREAT|0_EXCL);
# Установить обработчик, который удаляет временный файл tt при нормальном или аварийном завершении программы
END { unlink($name) or die "Couldn't unlink $name : $!" } # Перейти к использованию файла....

Комментарий

Если все, что вам нужно, - область для временного хранения данных, воспользуй­тесь методом new_tmpfile модуля IO::File. Он возвращает файловый манипуля­тор для временного файла, открытого в режиме чтения/записи фрагментом сле­дующего вида:
for (;;) {
$name = tmpnam();
sysopen(TMP, $tmpnam, 0_RDWR | 0_CREAT | 0_EXC) && last;
} unlink $tmpnam;
Файл автоматически удаляется при нормальном или аварийном завершении программы. Вам не удастся определить имя файла и передать другому процессу, потому что у него нет имени. В системах с поддержкой подобной семантики имя удаляется еще до завершения метода Ниже показан пример практического применения new_tmpfile. Мы создаем временный файл, выполняем запись, возвращаемся к началу и выводим записан­ные данные:
use 10::File;
$fh = 10::File->new_tmpfile or die "10::File->new_tmpfile: $!";
$fh->autorlush(1);
print( $fh "$i\n" while $i++ < 10;
seek($fh, 0, 0);
print "Tmp file has: ", <$rh>;
Во втором варианте создается временный файл, имя которого можно передать другому процессу. Мы вызываем функцию POSIX: :tmpnam, самостоятельно открыва­ем файл и удаляем его после завершения работы. Перед открытием файла мы не проверяем, существует ли файл

> Смотри также --------------------------------
Документация по стандартным модулям IO::File и POSIX; рецепт 7.19; стра­ница руководства tmpnam{3) вашей системы. Но перед вызовом ехес следует присвоить УР хотя бы fileno($fh). 2 См. рецепт 19.4.

7.6. Хранение данных в тексте программы

Проблема

Некоторые данные должны распространяться вместе с программой и интерпрети­роваться как файл, но при этом они не должны находиться в отдельном файле.

Решение

Лексемы __DATA__ и __END__ после исходного текста программы отмечают начало блока данных, который может быть прочитан программой или модулем через фай­ловый манипулятор DATA. В модулях используется лексема __DATA__:
while () { # Обработать строку }
„DATA__ и Данные
Аналогично используется __END__ в главном файле программы:
while () {
# Обработать строку
}
-END__
# Данные

Комментарий

Лексемы __DATA__ и __END__ обозначают логическое завершение модуля или сценария перед физическим концом файла. Текст, находящийся после __ОАТА__ или __END__, может быть прочитан через файловый манипулятор DATA уровня па­кета. Предположим, у нас имеется ги Лексема __END__ представляет собой синоним __DATA__ в главном пакете. Текст, следующий после лексем __END__ в модулях, недоступен. Появляется возможность отказаться от хранения данных в отдельном файле и перейти к построению автономных программ. Такая возможность нередко ис­пользуется для документирования. Иногда в программах хранятся конфигураци­онные или старые тестовые данные, исп Манипулятор DATA также применяется для определения размера или даты по­следней модификации текущей программы или модуля. В большинстве систем пе­ременная $0 содержит полное имя файла для работающего сценария. В тех системах, где значение $0 оказывается не
use POSIX qw(strftime);
$raw_time = (stat(DATA))[9];
$size = -s DATA;
$kilosize = int($size / 1024) . 'k';
print "

Script size is $kilosize\n";
print strftime("

Last script update: %c (%Z)\n", localtime($raw_time));
__DATA__
DO NOT REMOVE THE PRECEDING LINE Everything else in this file will be ignored.

> Смотри также ---------------------------
Раздел «Scalar Value Constructors» perldata(1).


Используются технологии uCoz