Анимация
JavaScript


Главная  Библионтека 

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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

8.5. Чтение из дополняемого файла 299

Комментарий

при достижении конца файла во время чтения устанавливается внутренний флаг, который препятствует дальнейшему чтению. Для сброса этого флага проще всего воспользоваться методом clearer г, если он поддерживается (присутствует в модулях IO::Handle и FileHandle). Кроме того, можно вызвать метод POSIX clearerr;

Snaptime = 1, use 10 Handle,

open (LOGFILE, /tmp/logfile ) or die can t open /tmp/logfile S for (,,) {

while (<LOGFILE>) { print } # Или другая операция sleep Snaptime,

LOGFILE->clearerr(), # Сбросить флаг ошибки ввода/вывода

Если простейший вариант в вашей системе не работает, воспользуйтесь функцией seek. Приведенный выше фрагмент с seek пытается переместиться на О байт от текущей позиции, что почти всегда завершается успехом. Текущая позиция при этом не изменяется, но зато для манипулятора сбрасывается признак конца файла, благодаря чему при следующем вызове <LOGFILE> будут прочитаны новые данные.

Если и этот вариант не работает (например, из-за того, что он полагается на так называемую «стандартную» реализацию ввода/вывода библиотек С), попробуйте следующий фрагмент - он явно запоминает старую позицию в файле и напрямую возвращается к ней:

for (,,) {

for (Scurpos = tell(LOGFILE), <LOGFILE>, Scurpos = tell(LOGFILE)) { # Обработать $

sleep Snaptime,

seek(LOGFILE, Scurpos, 0), # Вернуться к прежней позиции

Некоторые файловые системы позволяют удалить файл во время чтения из него. Вероятно, в таких случаях нет смысла продолжать работу с файлом. Чтобы программа в подобных ситуациях завершалась, вызовите stat для манипулятора и убедитесь в том, что количество ссылок на него (третье поле возвращаемого списка) не стало равным нулю:

exit If (stat(L0GFILE))[3] == О

Модуль File;;stat позволяет записать то же самое в более понятном виде:

use File stat,

exit if stat(.LOGFILE)->nlink == 0,

t> Смотри также-

Описание функции seek вperlfunc(i); документация по стандартным модулям POSIX и IO::Seekable; страницы руководства tail(i) и stdio(3).



8.6. Выбор случайной строки из файла

Проблема

Требуется прочитать из файла случайную строку.

Решение

Воспользуйтесь функцией rand п переменной $ (текущим номером строки):

srand,

rand($ ) < 1 && (Sline = $ ) while о, # $line - случайно выбранная строка

Комментарий

Перед вами - изящный и красивый пример неочевидного решения. Мы читаем все строки файла, но не сохраняем их в памяти. Это особенно важно для больших файлов. Вероятность выбора каждой строки равна 1/N (где N - количество прочитанных строк).

Следующий фрагмент заменяет хорошо известную программу fortune: $/ = %%\п ,

Sdata = /usr/share/games/fortunes , srand,

rand($ ) < 1 && (Sadage = $ ) while <>, print Sadage,

Если вам известны смещения строк (например, при наличии индекса) и их общее количество, можно выбрать случавшую строку н перепги непосредственно к ее смещению в фа11ле. Впрочем, индекс доступен далеко не всегда.

Приведем более формальное пояснение работы данного алгоритма. Функция rand ($ ) выбирает случайное число от О до текущего номера строки. Строка с номером N сохраняется в возвращаемой переменной с вероятностью 1/N. Таким образом, первая строка сохраняется с вероятностью 100%, вторая - с вероятностью 50%, третья - 33% и т. д. Вопрос лишь в том, насколько это честно для любого положительного целого N.

Начнем с конкретных примеров, а затем перейдем к абстрактным.

Разумеется, для файла из одной строки (N=1) все предельно честно: первая строка сохраняется всегда, поскольку 1/1 = 100 %. Для файла из двух строк N = 2. Первая строка сохраняется всегда; когда вы достигаете второй строки, она с вероятностью 50 % заменяет первую. Следовательно, обе строки выбираются с одинаковой вероятностью, и для N = 2 алгоритм тоже работает корректно. Для файла из трех строк N = 3. Третья строка сохраняется с вероятностью 1/3 (33 %). Вероятность выбора одной из двух первых строк равна 2/3 (66 %). Но как показано выше, две строки имеют одинаковую вероятность выбора (50 %). Пятьдесят процентов от 2/3 равны 1/3. Таким образом, каждая из трех строк файла выбирается с вероятностью 1/3.

В общем случае для файла из N-t-1 строк последняя строка выбирается с вероятностью l/(N-t-l),,a одна из предыдущих строк - N/(N-t-l). Деление N/(N-(-1) на



8.8. Чтение строки с конкретным номером 301

N дает вероятность 1/(N+1) для каждой нз N первых строк и те же 1/(N+1) для строки с номером N+1. Следовательно, алгоритм корректно работает для любого положительного целого N.

Нам удалось случайным образом выбрать из файла строку со скоростью, пропорциональной количеству строк в файле. При этом максимальный объем используемой памяти даже в худшем случае равен размеру самой длинной строки.

> Смотри также-

Описание специальной переменной $ вperlvar(i); рецепты 2.7-2.8,

8.7. Случайная перестановка строк

Проблема

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

Решение

Прочитайте все строки в массив, перетасуйте элементы массива (см. рецепт 4.17) и запишите полученную перестановку:

# Используется функция shuffle из главы 4 while (<INPUT>) { push(ialines, $ ),

(areordered = shuffle((ailines), foreach ((areordered) { print OUTPUT

Комментарий

Самое простое решение - прочитать все строки файла и переставить их в памяти. Смещения строк в файле неизвестны, поэтому нельзя перетасовать список с номерами строк и затем извлечь строки в порядке их появления в файле. Впрочем, даже при известных смещениях такое решение, вероятно, будет работать медленнее, поскольку придется многократно перемещаться по файлу функцией seek вместо простого последовательного чтения.

> Смотри также-

Рецепты 2.7-2.8; 4.17.

8.8. Чтение строки с конкретным номером

Проблема

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



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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242