Вход / изход
- Входно/изходните (IO, Input/Output) операции позволяват на процеса да обменя данни с външния свят
- Виртуалната машина съдържа допълнителен IO слой, през който минават IO операциите
- позволява по-голяма преносимост между различните ОС
- управлява се чрез служебни променливи и модула IO::Handle
- има възможност за прескачането му чрез употребата на определен набор функции
Filehandle
- Filehandle
- При IO операции (четене/писане) се борави с filehandle-a вместо с името на файла
- Filehandle-ите се именуват без специален префикс, прието е изцяло с главни букви
- @FH # невалиден, има префикс за масив
filehandle # валиден файлов хендъл
FileHandle # валиден файлов хендъл
FILEHANDLE # валиден файлов хендъл,
# предпочитана форма на именуване
Отваряне на файл
- Отварянето асоциира filehandle-а с реалния файл, връща true при успех, false при грешка и установява
$!
- Оператор за отваряне -
open
open FILEHANDLE
open FILEHANDLE,EXPR
open FILEHANDLE,MODE,EXPR
open FILEHANDLE,MODE,EXPR,LIST
MODE указва различните режими на отваряне.
EXPR се оценява и се счита за име и път към файла.
open може да изпълнява програми!
Отваряне на файл, форми на open
open FILEHANDLE - търси името на файла в скалара с име $FILEHANDLE.
- open (FH); # отваря файл с име оценката на $FH
open FILEHANDLE,EXPR - отваря файл с име EXPR, може да указва режим за отваряне.
- open (FH, ">somefile.txt"); # отваря за писане somefile.txt
open FILEHANDLE,MODE,EXPR - режимът се указва отделно.
open FILEHANDLE,MODE,EXPR,LIST - LIST съдържа параметри към стартираната програма
- open (PR, "-|", "ls", "-la");# изпълнява програмата ls
print <PR>; # с параметри -la и предоставя
close (PR); # резултата за четене от PR
Режими на отваряне - MODE (1/2)
- Режимът на отваряне указва дали ще четем, пишем, добавяме към файла или пък ще изпълним програма, с чийто вход/изход ще извършваме операции
- В двойната форма на
open FILEHANDLE, EXPR седи като първи символ от EXPR, при липсата му се подразбира отваряне за четене
- В тройната и четворната форма седи като отделен параметър на
open, което прави тези форми по-ясни и сигурни
Режими на отваряне - MODE (2/2)
- Част от режимите, включват
< - отваряне за четене, без право на писане
> - отваряне за писане, презаписва файла, ако вече съществува иначе го създава
>> - отваряне за добавяне, създава файла ако не съществува
+< - отваряне за четене и писане, не унищожава файла
+> - отваряне за четене и писане, файла се изтрива и презаписва ако съществува
+>> - отваряне за четене и добавяне след края, рядко има смисъл
-| - изпъленение на програма, четем изхода й
|- - изпъленение на програма, пишем към входа й
Затваряне на файл
- Оператор за затваряне -
close
close FILEHANDLE - затваря файла асоцииран с FILEHANDLE и записва буферите
close - затваря текущо избрания файлов хендъл (последния използван)
Специфики при отваряне/затваряне
- Служебни променливи
$. ($INPUT_LINE_NUMBER) - текущ ред на последния достъпен FILEHANDLE
$| ($OUTPUT_AUTOFLUSH) - автоматичен flush на буферите след операция за писане
- Файловете се затварят автоматично след края на програмата
open върху отворен файлов хендъл го затваря автоматично, но не нулира $.
Примери за open
- # двойна форма, отваряне за четене по подразбиране
open (FH, "larodi.txt");
# двойна форма, явно указване на режим "четене"
# проверка за резултат
open (FH, ">larodi.txt") || die "Error: $!";
# двойна форма, с оценяване на променлива, опасно!
open (FH, ">$filename") || die "Error: $!";
# двойна форма за изпълнение на програма
open (FH, "ls -la|") || die "Error: $!";
# тройна форма, отваряне за четене и писане
open (FH, "+<", $filename) || die "Error: $!";
# четворна, по-сигурна форма за изпълнение на програми
$program = "ls"; @params = qw {-l -a};
open (FH, "-|", $program, @params) || die "Error: $!";
Сигурна форма на open
open FILEHANDLE, MODE, EXPR
open FILEHANDLE, MODE, EXPR, LIST
- не позволява атаки през имената на файловете (чрез добавяне на символи за режима на отваряне), тъй като той e явно указан чрез 2-рия параметър
- при изпълнение на програми с параметри трябва да се използва 4-ната форма, не позволява скрити pipe-ове
- $program = "ls -l -a | rm -rf /"; # скрит pipe
open (FH, "-|", $program) || die "Error: $!"; # изпълнява и rm
Пример за лоша практика при работа
с файлове
- #!/usr/bin/perl
# Четем името на файла от STDIN (не винаги!),
# но не проверяваме за специални символи
$filename = <>;
# Разчитаме да отворим за четене, но $filename може да
# съдържа всичко, вкл. |, което да изпълни програма
open (FH, $filename ); # не проверяваме резултата от open
# Четем и извеждаме, може да презапишем файл,
# да отпечатаме конфиденциална информация
print (<FH>);
close (FH); # затваряме без проверка от резултата
Оператори за вход/изход
<>, print, printf по подразбиране работят с файловете в текстов режим
- Диамантения оператор
<FILEHANDLE>, чете от отворен файл хендъл и връща един прочетен ред
- В скаларен контекст, чете по един ред, до символ за край на реда
$/ ($INPUT_RECORD_SEPARATOR) - разделител между редовете
- В списъчен контекст, чете до символ за края на файл (EOF)
Особености на диамантения оператор
- По принцип, ако върнатата от него стойност не бъде присвоена на променлива, тя не отива в
$_
- Ако диамантеният оператор (или присвояване на стойността му) е единственото условие на цикъл:
- неприсвоената му стойност отива в
$_
- условието се счита за изпълнено, ако върната от диамамантения оператор стойност е дефинирана
- предпазва ни от ред, съдържащ само
0 (без символ за нов ред)
- # следните редове вършат едно и също...
while (<STDIN>) { print; }
while (defined($_ = )) { print; }
for (;<STDIN>;) { print; }
while (my $line = <STDIN>) { print $line }
Стандартни файлови потоци
- Три стандартни файлови хендъла са отворени при стартиране на програмата
- Повечето оператори за вход/изход ги използват по подразбиране
- print "Hello world!\n"; # извежда към STDOUT
die "What a nasty error: $!\n"; # извежда към STDERR
Четене/писане от и към стандартните
файлови потоци (1/4)
- Диамантеният оператор
<>
- без подаден файлхендъл
STDIN - с явно подаден файлхендъл към стандарния вход
- Операторите за изход
print, printf
- без подаден файлхендъл
STDOUT, STDERR - с явно подадени като параметър файлхендъл към стандартния изход или стандартния изход за грешки
Пример за вход/изход от
стандарните потоци
- # Четене на данни от STDIN в скаларен контекст
print "Enter data ('ENTER' for end):\n";
$a = <STDIN>;
# "Скрито" писане към STDOUT по подразбиране
print "Inputted: $a \n";
# Четене на данни от STDIN в списъчен контекст
print "Enter data ('EOF' symbol for end):\n";
@b = <STDIN>;
# Явно указване за писане към STDOUT
# Не е нужно
print STDOUT "Inputted:\n", @b;
Четене/писане от и към
стандартните файлови потоци (2/4)
- Диамантеният оператор има специфично поведение!
- Без подаден параметър, чете:
- от
STDIN, ако на програмата не са подадени параметри на команден ред
- последователно от файловете с имена параметрите подадени на команден ред (съдържат се в служебния масив
@ARGV)
- От shell (обкръжението) обикновено можем да изпратим данни към
STDIN, STDOUT посредством >, < и | (pipe)
- myprogram.pl < input.txt
cat input.txt | some.perl.code.pl
program.generating.output.pl | program.getting.input.pl
Четене/писане от и към
стандартните файлови потоци (3/4)
- # Четене на данни в скаларен контекст
$a = <>;
print "Inputted: $a \n";
# Четене на данни в списъчен контекст
@b = <>;
print "Inputted:\n", @b;
# Масивът @ARGV съдържа параметрите подадени на всяка
# програма и може да бъде променян
@ARGV = ("example.0.pl","example.1.pl");
while (<>) {
print "line: $_";
}
Четене/писане от и към
стандартните файлови потоци (4/4)
- # Чете до EOF
print "Enter data:\n";
while (defined($line = <STDIN>)) {
print $line;
# или каквато и да е обработка на данните
}
print "\nEnter data:\n";
# Чете до EOF, но в по-кратък запис
while (<STDIN>) {
chomp;
print;
# или каквато и да е обработка на данните
}
Оператори за извеждане
print, printf, sprintf (1/5)
print FILEHANDLE LIST
- отпечатва списъка от аргументи
LIST към FILEHANDLE
print LIST
- отпечатва списъка от аргументи
LIST към STDOUT
print
Оператори за извеждане
print, printf, sprintf (2/5)
- Служебни променливи при извеждане
$, ($OUTPUT_FIELD_SEPARATOR) - разделител при извеждане на LIST
$\ ($OUTPUT_RECORD_SEPARATOR) - извежда се след края на списъка
- $, = ' + ';
$\ = ' ! ';
@larodi = qw { супер много лароди };
print @larodi;
# ще изведе
# супер + много + лароди !
Оператори за извеждане
print, printf, sprintf (3/5)
printf FILEHANDLE FORMAT, LIST
- извежда форматирания низ към
FILEHANDLE
printf FORMAT, LIST
- извежда форматирания низ към
STDOUT
- използвайте просто
print, когато може - по-четливо, бързо и защитено от грешки
Оператори за извеждане
print, printf, sprintf (4/5)
sprintf FORMAT, LIST
- връща като резултат форматирания низ
- някои често използвани служебни символи
- %% - знак за процент
- %c - character със съответния код
- %s - стринг
- %d - десетично число със знак
- %u - десетично число без знак
- %x - шестнайсетично число без знак
- %e - число с плаваща запетая в експоненциален вид
- %f - число с плаваща запетая в десетичен формат
Оператори за извеждане
print, printf, sprintf (5/5)
- # print връща 1, ако е извела успешно съобщението
$a = print("hello ", "chixen", "\n");
# print може да получава списък от параметри, но грешно
# поставените скоби променят контекста
print (2+3),"hello\n"; # грешно, принтира само 5
print ((2+3),"hello\n");
print 2+3,"hello","\n";
print +(2+3), "hello\n"; # + форсира скаларен контекст
# printf извежда форматиран стринг и връща 1 при успех
printf ("%.2f\n", 3.3-2);
# sprintf връща форматиран низ,
# тя не е операция за изход
print sprintf ("%.2f\n", 3.3-2);
Произволен (непоследователен)
достъп до файлове
- Работи се върху стандартните file handle-и, но се използват оператори установяващи файловия указател за позиция или четене на блокове
- В текстов режим спокойно може да се изпозлват
print, printf, <>, като четенето/писането започва от текущата позиция и разделителите се вземат в предвид
Оператори за непоследователен достъп
seek FILEHANDLE,POS,WHENCE
WHENCE - 0 от началото, 1 от текущата, 2 от края на файла
tell FILEHANDLE, tell - връща текуща позиция за съответния хендъл или за последния използван
getc FILEHANDLE, getc - връща следващия символ
read FILEHANDLE,SCALAR,LENGTH,OFFSET
read FILEHANDLE,SCALAR,LENGTH - чете LENGTH байта (или символa при специфично отваряне на файла) от текущата позиция, записва ги в SCALAR, като започва от началото или от зададения OFFSET
eof FILEHANDLE, eof - връща истина при последващ край на файла
Системни функции за работа с файлове
- Прескачат IO слоя и викат системните функции
- боравят с блокове от данни като
read
- системно зависими, ползват се рядко, предимно при работа с големи блокове от данни
sysopen FILEHANDLE,FILENAME,MODE,PERMS
- системно зависимо,
MODE от Fcntl
sysread FILEHANDLE,SCALAR,LENGTH,OFFSET
syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET
sysseek FILEHANDLE,POSITION,WHENCE
Работа с файловете от
двоична гледна точка
binmode FILEHANDLE, LAYER
- установява двоичен режим на четене на файловете, за ОС, където е възможно
- добре е да се ползва винаги, когато четете двоични файлове (например картинки)
- прескача специалните символи за край на файл (за проверка за достигнат край, се използва върната стойност от
read или sysread)
LAYER - променя контекста, в който се разглеждат данните, използва се и при open, свойство на PerlIO layer
- За четене се използва
read, sysread, за писане print, syswrite, съответно при работа с PerlIO и системните функции
Пример за работа с
двоични файлове
- # показва що е лароди на снимка към STDOUT ;)
$filename = 'larodi.jpg';
open(JPG, '<', $filename) || die "Error: $!";
# не се губят специалните символи за край на ред и файл,
# нито при четене, нито при писане, тъй като сме
# установили двоичен режим
binmode(JPG);
binmode(STDOUT);
# четем на блокове по 8 килобайта
while (read(JPG, $buff, 8 * 2**10)) {
print STDOUT $buff;
};
close (JPG);
Fileglobs
- Бърз начин за намиране файлове с определени имена от дадена директория
- По подразбиране от текущата директория
- Вътрешно се използва
glob EXPR (File::Glob)
@c_files = glob ("*.c");
@perl_files = glob("*.pl");
- Алтернативно приложение на синтакса
<>
- Без кавички
@all_files = <*>;
- Използването на
<EXPR> в glob вариант няма нищо общо с четене от файл или от filehandle
Проверки върху файлове (1/2)
- Операторът
-x тества файлове за дадени свойства
- Форми
-X FILEHANDLE - тества обекта, към който сочи FILEHANDLE
-X EXPR - третира EXPR като пък тъм обекта
-X - тества $_, освен при -t, където се проверява стандартния вход
Проверки върху файлове (2/2)
- Над 20 оператора, някои от които
-e - съществуване на файл/директория
-z - нулева дължина
-s - ненулева дължина (размер)
-d - директория
-x - изпълним
-r - имаме ли права за четeне
-w - имаме ли права за писане
-T - ASCII Text (евристика)
- $filename = <STDIN>;
print "It's a directory!" if -e $filename && -d $filename;
Изтриване/преименуване
на файлове
- Изтриване - оператор
unlink
unlink
unlink LIST
- Връща броя на успешно изтритите
- Не се изпозлва за директории!
- Преименуване - оператор
rename
rename OLDNAME,NEWNAME
- презаписва
NEWNAME при съвпадение
- системно зависима
- "Отрязване на файл"
truncate FILEHANDLE,LENGTH
truncate EXPR,LENGTH
- изтрива съдържанието на файла след дадената дължина
Работа с директории (1/2)
DIRHANDLE - вътрешен индентификатор, подобен на файлов хендъл, но за работа с директории
chdir EXPR, chdir - смяна на текущата директория към EXPR или $ENV{HOME}
mkdir FILENAME,MASK
mkdir FILENAME - създава директория
rmdir FILENAME, rmdir - премахва директория
Работа с директории (2/2)
opendir DIRHANDLE,EXPR - отваря директория с името оценката на EXPR към DIRHANDLE
closedir DIRHANDLE - затваря директорията
readdir DIRHANDLE
- в скаларен контекст връща следващия обект от директорията
- в списъчен връща всички обекти в директорията
. и .. са обекти във всяка директория
Непоследователно четене
на директория
telldir DIRHANDLE - текуща позиция на четене от readdir
seekdir DIRHANDLE,POS - установява текуща позиция за четене от readdir
rewinddir DIRHANDLE - установява текуща позиция за четене в началото на директорията
- Може да възникнат проблеми при успоредна промяна на съдържанието на директорията