Анимация
JavaScript
|
Главная Библионтека Ведение базы данных о времени правильного ввода секретных слов Давайте проследим за тем, когда каждый пользователь последний раз правильно вводил свой вариант секретного слова. Очевидно, единственная структура данных, которая годится для этого - хеш. Например, оператор $last good{$name) = time; присваивает значение текушего времени, выраженное во внутреннем формате (некоторое большое целое, свыше 800 миллионов, число, которое увеличивается на единицу каждую секунду) элементу хеша %last good, имеющему указанное имя в качестве ключа. В последующем это даст базу данных о времени последнего правильного ввода секретного слова каждым из пользователей, который вызывал эту программу. При этом, однако, в промежутках между вызовами программы хеш не существует. Каждый раз, когда программа вызывается, формируется новый хеш, т.е. по большому счету мы всякий раз создаем одноэлементный хеш, а по завершении выполнения программы немедленно про него забываем. Функция dbmopen* отображает хеш в файл (фактически в пару файлов), известный как DBM-файл. Она используется следующим образом: dbmopen (%last good,"lastdb",0666) И die "cant dbmopen lastdb: $!", $last good($narae) = time; dbmclose (%last good) It die "cant dbrnclose lastdb: $!"; Первый оператор выполняет отображение, используя имена файлов lastdb.dir и lastdb.рад (это общепринятые имена для файлов lastdb, образующих DBM-файл). Если эти файлы необходимо создать (а это бывает при первой попытке их использования), то для них устанавливаются права доступа 0666**. Такой режим доступа означает, что все пользователи могут читать и осуществлять запись в эти файлы. Если вы работаете в UNIX-сис-теме, то описание битов прав доступа к файлу вы найдете на man-странице chmod{2). В других системах chmodQ может работать так же, а может и не работать. Например, в MS-DOS для файлов не устанавливаются права доступа, тогда как в Windows NT - устанавливаются. Если уверенности нет, прочтите описание версии вашей системы. Второй оператор показывает, что мы используем этот преобразованный хеш как первоначальный. При этом, однако, при создании или корректировке какого-либо элемента хеша автоматически обновляются файлы, образующие DBM-файл. При последующем обращении к хешу значения его элементов поступают непосредственно из его отображения на диске. Благодаря этому * Можно также использовать низкоуровневую функцию tie с конкретной базой данных; этот вариант подробно описан в главах 5 и 7 книги Programming Perl и на man-страницах perlite(\) и AnyDMBJile (3). ** Фактически права доступа к этим файлам определяются в результате выполнения логической операции И над числом 0666 и текущим значением переменной umask вашего процесса механизму хеш физически существует и в периоды между вызовами данной программы. Третий оператор отсоединяет хеш от DBM-файла, делая это практически так же, как операция close закрывает файл. Хотя все вновь введенные нами операторы позволяют вести базу данных (и даже обеспечивают ее создание), у нас пока нет никакого способа получить имеющуюся в этой базе данных информацию. Для этого придется создать отдельную программку, которая выглядит примерно так: #!/usr/bin/perl dbmopen (%last good, "lastdb", 0666) die "cant dbmopen lastdb: $!"; foreach $name (sort Iceys (%last good) ) ( $when = $ last good{Sname}; $hours = (timeO - $when) / 3600; # вычислить истекшее время в часах write; format STDOUT = User @<«<<«<<<<: last correct guess was (?<<< hours ago. $name, $hours Здесь мы осуществляем несколько новых операций: выполняем цикл foreach, сортируем список и получаем значения ключей массива. Сначала функция keys принимает имя хеша в качестве аргумента и возвращает список значений всех ключей этого хеша в произвольном порядке. Для хеша %words, описанного ранее, результат будет примерно таким: fred, barney, betty, wilma, причем имена могут быть перечислены в произвольном порядке. В случае хеша %last good результатом будет список всех пользователей, которые правильно ввели свои секретные слова. Функция sort сортирует этот список в алфавитном порядке (как если бы вы пропустили текстовый файл через фильтр sort). Это гарантирует, что список, обрабатываемый следующим далее оператором foreach, всегда будет отсортирован по алфавиту. Уже упомянутый Perl-оператор foreach очень похож на оператор foreach shell С. Он получает список значений и присваивает каждое из них по очереди какой-либо скалярной переменной (в нашем случае - переменной $name), выполняя для каждого значения по одному разу проход цикла (блок). Так, для пяти имен в списке %last good мы делаем пять проходов, при этом каждый раз переменная $name имеет другое значение. При выполнении цикла foreach происходит загрузка пары переменных, используемых затем в формате stdout, и вызывается этот формат. Обратите внимание: мы вычисляем "возраст" файла, вычитая записанное (в массиве) системное время из текущего времени (которое возвращает функция time), а затем деля результат на 3600 (чтобы преобразовать секунды в часы). в Perl используются также удобные способы создания и ведения текстовых баз данных (например, файла паролей) и баз данных с фиксированной длиной записей (таких, как база данных "последней регистрации", которую ведет программа login). Эти способы описаны в главе 17. Окончательные варианты программ Здесь вашему вниманию предлагаются программы, которые мы писали в этой главе, в окончательном виде. Сначала - программа-приветствие: #I/usr/bin/perl &init words ( ) ; print "what is your name " ; $name = <STDIN>; chomp ($name); if ($name =~ /randalXb/i) { # обратно на другой путь :-) print "Hello, Randal How good of you to be here\n"; ) else { print "Hello, $name \n"; # обычное приветствие print "What is the secret word? "; $guess = <STDIN> ; chomp $guess ; while { good word( $name, $guess)) { print "Wrong, try again. What is the secret word? "; $guess = <STDIN> ; chomp $guess; dbmopen {%last good, "lastdb", 0666); $last good($name) = time; dbmclose (%last good); sub init words ( while ($filename = <:*.secret>) ( open (WORDSLIST, $filename) I I die "cant open $filename: $"; if (-M WORDSLIST < 7) { while {$name = <WORDSLIST>) ( chomp ($name) ; $word = <WORDSLIST> ; chomp (Sword); $words {$name) = $word ; ) else ( # rename the file so it gets noticed rename ($filename, "$filename.old") I I die "cant rename $filename.old: $"; close WORDSLIST; sub good word { my($somename,Ssomeguess) = @ ; # перечислить параметры Ssomename =~ s/\W.* ; # удалить все символы, стоящие после первого слова $somename =~ tr/A-Z/a-z/; # перевести все символы в нижний регистр if ($somename eq "randal") { # не нужно угадывать 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [ 16 ] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |