от Курс за ССОК
Увод в програмирането на Perl
Лекция #8 Работа с файлове и директории
Съхранението на данни в променливи е временно – тези данни се губят, когато програмата завърши. Файловете перманентно съхраняват голям обем от данни. В тази лекция ще разгледаме как файловете биват създавани, обновявани и обработвани от програми писани на Perl. Ще разгледаме както файлове с последователен достъп до информацията в тях, така и такива с произволен(директен) достъп.
Perl не налага никаква структура на файловете. Той ги счита за поредица от знаци. На практика всички файлове в Perl могат да бъдат считани за текстови файлови.
Perl разглежда всеки файл просто като последователен поток от знаци. Всеки файл завършва със знак за край на ред(end-of-file marker). Когато се отвори файл с него се асоциира файлова дръжка(filehandle). Тя се използва за обръщане към отворения файл, когато програмиста иска да чете от него или да пише в него. Файловите дръжки в този смисъл са подобни на други променливи с които сме се сблъсквали с тази разлика че те не са предшествани от никакъв типов спецификатор. По тази причина имената им обикновено се изписват с големи букви за четливост.
Файловите дръжки не винаги са асоциирани с файлове на диск. Всъщност три файлови дръжки се отварят автоматично, когато започне програмата своето изпълнение: STDIN, STDOUT и STDERR. Вече сме се сблъсквали с STDIN(стандартен вход) и сме го използвали в комбинация с оператора <>. STDOUT означава стандартен изход – той обикновено е екрана. Функцията print обикновено отпечатва изхода си в STDOUT, макар че досега не сме използвали файловата дръжка изрично като аргумент. STDERR означава стандартна грешка и отбелязва мястото, където програмата следва да отпечатва своите съобщения за грешка. Както и при стандартния изход това място обикновено е екрана.
За да можем да използваме файл в програма на Perl, файлът трябва първо да бъде отворен и да бъде асоцииран с файлова дръжка. Процедурата обикновено се постига чрез функцията open. Твърдение open нормално има следния вид:
open(HANDLE, 'filename') or die("Cannot open file : $!");
Функцията open приема два аргумента. Първия е името на файловата дръжка, която ще използваме за да се обръщаме към файла, а втория – името на самия файл. Името на файла може да включва пълен път до файла който да помогне на Perl да намери файла. Ако името на файла е подадено без пълен път до него Perl приема че файла се намира в същата директория, в която се намира и програмата. Програмата може да обработва повече от един файл – всеки файл трябва да бъде отворен със своя уникална файлова дръжка.
Файловете могат да бъдат отваряни в различни режими като пред името им се добавя съотвения префикс. За да създадете нов файл или да унищожите старото съдържание на съществуващ файл отворете този файл за писане(">име на файл"). За да четете от даден файл отворете го в режим за четене("<име на файл" или просто "име на файл"). За да добавяте данни в края на файл отворете го в режим за добавяне(">>име на файл"). Трябва да отбележим и че отварянето на несъществуващ файл в режим за добавяне ще създаде файла. За да използваме файл за четени и запис трябва да отворим в режим за обновяване с +< или +>. Режимът +< отваря файл за четене и запис като запазва неговото съдържание. Режимът +> създава файл за четене/запис или ако той вече съществува файла бива отворен, а текущото му съдържание – унищожено. Ако искате да четете/пишете в съществуващ файл почти винаги ще използвате режима +< вместо +>.
Отворените файлови дръжки се затварят с функцията close:
close(FILE) or die("Cannot close file : $!");
Perl автоматично затваря всички файлови дръжки, когато завърши програмата, така обаче, ако се получат някакви грешки при затварянето на файловете, съобщенията за тези грешки ще бъдат изгубени.
За да прочетем информация последователно от файл, програмата обикновено започва да чете в началото на файла и прочита последователно всички данни докато не намерим желаните от нас. По-рано видяхме как това става от STDIN посредством оператора диамант <>. Този оператор прочита данни докато не стигне до знак който съвпада със стойността на специалния оператор $/. По подразбиране стойността в $/ е "\n", така че оператора ще прочете един ред данни. Ако оператора <> се използва в списъчен контекст той ще върне толкова редове колкото са елементите в списъка. Оператора <> може да се използва за четене от произволни файлови дръжки – просто името на файловата дръжка се поставя между <>.
Пример:
#!/usr/bin/perl
use warnings;
use strict;
open(IN, "in.txt") or die("Cannot open in.txt for reading: $!");
while (my $line = <IN>) {
print("file : $line\n");
}
close(IN) or die("Cannot close file : $!");
Пример:
#!/usr/bin/perl
use warnings;
use strict;
print("Opening file for output\n");
open(OUTFILE, ">file.txt") or die("Can't open file file.txt : $!");
print("Outputting to file\n");
print(OUTFILE "They say we're young\n");
close(OUTFILE) or die("Cannot close file.txt : $!");
print("The file now contains:\n");
open(INFILE, "file.txt") or dir("Cannot open file.txt : $!\n");
print while (<INFILE>);
close(INFILE) or die("Cannot close file.txt : $!");
print("\nAppend to the end of the file\n");
open(OUTFILE, ">>file.txt") or die("Cannot open file.txt : $!");
print(OUTFILE "and we don't know...\n");
close(OUTFILE) or die("Cannot close file.txt : $!");
print("It now reads:\n");
open(INFILE, "file.txt") or die("Cannot open file.txt : $!");
print while (<INFILE>);
close(INFILE) or die("Cannot close file.txt : $!");
Файловите дръжки не могат да се подават като аргументи на функции. Има обаче начини да бъде заобиколен този проблем. Един от тях е на функцията да се подаде като аргумент името на файла и тя да го отвори и да асоциира с него файлова дръжка.
Когато никаква файлова дръжка не е упомената в оператора <> Perl взема името на файл от специалната променлива @ARGV. Този масив съдържа всички аргументи подадени на програмата от командния ред. Например ако изпълним една програма така perl primer3.pl in.txt аргумента от командния ред се явява низа непосредствено след името на програмата. Всъщност оператора <> ще обработи подред всички стойности в масива @ARGV. Ако на програмата не бъдат подадени аргументи от командния ред <> ще използва по подразбиране
файловата дръжка STDIN.
Пример:
#!/usr/bin/perl
use warnings;
use strict;
print while (<>);
Оператора <> не е единствения начин да се чете от файл. Както винаги в Perl има повече от един начин това да се направи. Може да четете от файл в друг формат освен по редове. Например извикването на функцията read(FILE, $input, 80) прочита 80 байта от FILE и ги запазва в $input. Този метод е полезен когато знаете предварително колко точно данни трябва да прочетете в даден момент. Може също така да четете и по един знак от файл с функцията getc. Функцията getc(FILE) ще прочете един знак от файл и ще върне този знак. А ако искате да прочетете целия файл наведнъж. Програмистите на Perl наричат това действие сърбане на файл(slurping). Един начин да се изсърба файл е да се използва оператора <> в списъчен контекст. Друг начин да се изсърба файл е да се промени стойността на променливата $/ - ако стойността и се установи на undef оператора <> ще прочете целия файл и ще го запази като скалар.
Досега се занимавахме само с файлове с последователен достъп. Perl позволява да се създават и използват и файлове с случаен(директен) достъп. Отделните записи в такъв файл трябва да със еднакъв размер за да могат да бъдат достъпвани директно без да се обхождат другите записи във файла. Понеже всеки запис в такъв файл има еднаква дължина това прави позицията на всеки запис относителна спрямо началото на файла и тя може да бъде изчислена.
Пример:
#!/usr/bin/perl
use warnings;
use strict;
print("Creating a file with the numbers 0-9 in it.\n");
open(FILE, "+>rand.txt") or die("Unable to open file : $!");
print(FILE "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n");
close(FILE) or die("Unable to close file : $!");
open(FILE, "rand.txt") or die("Unable to open file : $!");
<FILE>;
my $length = tell(FILE);
print("The length of a record is : $length\n");
close(FILE) or die("Unable to open file: $!\n");
open(FILE, "+<rand.txt") or die("Unable to open file : $!");
print("The file contains:\n");
print while(<FILE>);
print("\nSeek to the sixth record\n");
seek(FILE, 5 * $length, 0);
print("Truncate file to ", tell(FILE), " bytes\n");
truncate(FILE, tell(FILE)) or die("Unable to truncate: $!");
seek(FILE, 0, 0);
print("The file now contains:\n");
print while (<FILE>);
print("\nSeek to the third record and print its contents: ");
seek(FILE, 2 * $length, 0);
my $in = <FILE>;
print("$in");
print("The remainder of the file from this point is:\n");
print while(<FILE>);
print("\nUpdating the second item with value 7\n");
seek(FILE, $length, 0);
print(FILE "7");
seek(FILE, 0, 0);
print("The file now contains:\n");
print while (<FILE>);
close(FILE) or die("Unable to close file : $!");
Функцията tell определя текущата позиция във файл. Функцията seek може да бъде използвана за намирането на всеки запис във файла. Тя приема три аргумента – файлова дръжка, отместване и метод. Метода показва откъде да се извършва търсенето – 0 указва търсенето да почне от началото на файла, 1 от текущата позиция във файла и 2 от края на файла.
Функцията truncate се използва за скъсяване на файл. Тя приема два аргумента – файлова дръжка и байтова позиция. Функцията премахва всичко във файла след тази позиция.
Perl предлага файлови тестове, които позволяват на програмата да открива информация за файл или директория:
- -r – имаме ли права за четене на файла
- -w – имаме ли права за писане във файла
- -e – съществува ли файла
- -z – проверява дали файла съществува и дали размера му е нула
- -s – проверява дали файла съществува и дали размера му е по-голям от нула. Връща размера на файла в байтове
- -f – проверява дали файла е обикновен
- -d – проверява дали файла е директория
- -T – Проверява дали файла е текстов
- -B – проверява дали файла е двоичен
- -M – време от последната промяна на файла
- -А – време от последното достъпване до файла
- -C – възраст на файла
- -x – изпълним ли е файла
Пример:
#!/usr/bin/perl
use warnings;
use strict;
foreach my $file(@ARGV) {
print("Checking $file : ");
if (-e $file) {
print("$file exists!\n");
if (-f $file) {
print("The file $file is : ");
print(" executable") if (-x $file);
print(" readable") if (-r $file);
print(" writable") if (-w $file);
print("\n");
print("It is ", -s $file, " bytes.\n");
my @time = timeconv(-A $file);
print("Last accessed at $time[0] days,", "$time[1] hours, $time[2] minutes ", "and $time[3] seconds\n");
@time = timeconv(-M $file);
print("Last modified at $time[0] days,", "$time[1] hours, $time[2] minutes ", "and $time[3] seconds\n");
} elsif (-d $file) {
print("$file is a directory!\n");
}
} else {
print("$file doesn't exist.\n");
}
print("\n");
}
sub timeconv {
my $time = shift();
my $days = int($time);
$time = ($time - $days) * 24;
my $hours = int($time);
$time = ($time - $hours) * 60;
my $minutes = int($time);
$time = ($time - $minutes) * 60;
my $seconds = int($time);
return ($days, $hours, $minutes, $seconds);
}
Правата за достъп до файл могат да се променят с функцията chmod. Въпреки че правата в крайна сметка се контролират от файловата ви система функцията chmod има достъп до тях и може да ги променя. Файловите права дават възможност на програмиста да ограничи потребителските права да чете, записва или изпълнява файл. Функцията chmod се извиква в следната форма:
chmod(mode, fileList);
Режима(mode) определя правата за всеки файл в списъка с файлове. В най-добрия случай режима представлява четирицифрено 8мично число. Първата цифра в него е 0 и тя показва че числото е осмично. Останалите три цифри показват на кого какви права да бъдат дадени. Втората цифра представлява правата на собственика на файла, третата тези на групата към която файла принадлежи, а последната показва правата за останалите потребители. Групи се използват за да могат много потребители да споделят едни и същи файлове.
Правата биват:
- 0 – потребителя не може да прави нищо
- 1 – потребителя може да изпълнява
- 2 – потребителя може да пише
- 3 – потребителя може да изпълнява и пише
- 4 – потребителя може да чете
- 5 – потребителя може да чете и изпълнява
- 6 – потребителя може да пише и чете
- 7 – потребителя има пълни права
Пример:
chmod(0777, myfile.pl);
chmod(0751, test.pl);
Функцията rename преименува файл. Функцията връща 1 при успех и 0 при неуспех.
rename(fileName, newFileName);
Пример:
#!/usr/bin/perl
use warnings;
use strict;
if (-e 'file.txt') {
print("Do you want to write over file.txt? (yes or no): ");
chomp(my $response = <STDIN>);
rename('file.txt', 'file.old') or die("Error renaming : $!")
if ($response eq 'no');
}
open(FILE, ">file.txt") or die("Error opening : $!");
print(FILE "A copy of file.txt is saved in file.old.\n");
close(FILE) or die("Cannot close: $!");
За да изтриете файл от директория използвайте функцията unlink.
unlink(listOfFiles);
Тя изтрива списък от файлове и връща броя на успешно изтритите. Ако не и се подадат аргументи използва за аргумент $_.
Пример:
#!/usr/bin/perl
use strict;
use warnings;
print("Input a file you want deleted: ");
chomp(my $file = <STDIN>);
if (-f $file && unlink($file)) {
print("The file $file was deleted successfully.\n");
} else {
print("It was not deleted: $!");
}
Функцията chown се използва в UNIX за промяна на собственика на файл:
chown(userID, groupID, fileList);
Пример:
chown(55, 100, 'file.txt');
Функцията utime модифицира времевите опчечатъци на файла:
utime(creation, lastModified, fileList);
Параметрите creation и lastModified трябва да са от тип интервал от време – брой секунди след полунощ на 1-ви Януари 1970(1904 за Mac OS) UTC. Обикновено програмистите модифицират стойността върната от функцията time, когато използват utime. Функцията time не приема аргументи и връща текущия интервал от време.
Пример:
$newtime = ( time() - (3600 * 72));
utime( $newtime, $newtime, 'file') ;
Възможно е да съществува повече от едно име за определен файл. Алтернативните имена могат да бъдат създавани, отваряни, унищожавани, преименувани и т.н. в Perl. Тази концепция за алтернативните имена се нарича свързване. Има два типа връзки: твърди връзки и символични връзки.
Твърдата връзка може да се разглежда като друго име за файла. Много твърди връзки могат да представят много имена на файла. Твърдата връзка не се отличава по нищо от оригиналния файл. Операционна система следи броя на твърдите връзки към всеки файл. Когато този брой стане равен на нула и файлът се затвори той се изтрива от диска. Твърдите връзки не могат да свързват файлове на различни файлови системи. Не могат да се създават твърди връзки към директории.
Символичната връзка е специален файл, който сочи към друг файл. Когато отворим символична връзка операционната система я следва и отваря файла към който тя сочи. Символичните връзки не участват в броя на връзките към файл. Те могат да сочат както файл, който не съществува, така и такъв на друга файлова система или пък директории. Изтриването на символичните връзки към файл не причинява изтриването на файла.
В Perl твърдите връзки се създават с функцията link, която приема два аргумента – оригиналното име на файла и името на новата връзка. Функцията symlink създава символични връзки – тя също приема два аргумента – оригиналното име на файла(не е задължително да съществува, може и да е име на директория) и името на символичната връзка. Символичните връзки могат да бъдат следвани с функцията readlink. Функцията unlink изтрива всички твърди връзки към файл, изтривайки го по този начин.
Може да избирате повече от едно име на файл със следния синтаксис:
@files = <*.txt>;
или
$glob = "*.txt";
@files = glob($glob);
Повече по тази тема ще говорим в една от следващите ни лекции.
Файловете имат файлови дръжки, а директориите – директорни дръжки. Те са почти идентични на файловите – могат да бъдат отваряни, затваряни, четени. В тях обаче не може да се запсива информация. Директорна дръжка се отваря с opendir и се затваря с closedir. opendir приема два параметъра – име на дръжката и име на директорията, която да се асоциира с нея. При нея не могат да се указват режими за четене, писане, добавяне и т.н. както при open.
След като директорната дръжка е отворена можем да използваме функцията readdir за да четем от нея. Тя обхожда директорията и връща имената на всички файлове и директории в нея. Функцията rewinddir връща директорната дръжка в начална позиция.
Функцията mkdir се използва за създаване на нова директория. Тя приема два аргумента – име на директорията и права за директорията. Текущата директория се променя с функцията chdir. Директории могат да се изтриват с функцията rmdir.
Тъй като Windows и UNIX имат различни разделите на пътищата в имената на директориите , ако правите приложение, което трябва да е портативно за вас полезна може да се окаже променливата $^О. Тя съдържа в себе си името на операционната система, на която програмата се изпълнява в момента.
Картинка:Пример.jpg