Комп'ютерні науки > Ідея буферизації
write ( E1 ); write ( E2 ); … ; write ( EN ). Виконання writeln відрізняється тим, що в буфер екрана "додається eol", і курсор переводиться в наступний рядок. Організація роботи з клавіатурою набагато складніше. Символи, утворені натисканням клавіш, накопичуються в буфері клавіатури. Він уміщає 15 символів. У цьому можна переконатися, запустивши програму uses crt; begin delay(16000) end. При її виконанні протягом 16 секунд неважко встигнути натиснути якусь клавішу 16 разів і за останнього натискання почути звуковий сигнал комп’ ютера, що свідчить про переповнення буфера клавіатури. Буфер переповнюється, оскільки за виконання цієї програми символи з нього не переносяться у внутрішній буфер. Крім того, набрані далі символи не відображаються на екрані і взагалі "зникають". Перенесення символів у внутрішній буфер відбувається за виконання процедур читання readln і read. Як і для інших текстів, його розмір 128 байтів. У цьому можна переконатися, запустивши програму begin readln end. За її виконання комп’ ютер починає чекати натискань на клавіші. Кожне натискання на клавішу (крім Enter) приводить до появи відповідного символу в буфері клавіатури. Цей символ одразу переноситься в її внутрішній буфер та через екранний буфер без затримки відображається на екрані. Після того, як набрано 128 символів, наступний символ до внутрішнього буфера не переноситься й на екрані не з’ являється. Натомість можна почути звуковий сигнал, що свідчить про переповнення внутрішнього буфера. Натискання на клавішу Enter веде до появи відповідного символу в буфері клавіатури та переведення курсора в новий рядок екрана. Коли цей символ з’ являється у внутрішньому буфері, рядок у ньому розглядається як "завершений символом eol". Завершений рядок у внутрішньому буфері аналізується й за сталими в ньому обчислюються значення базових типів та присвоюються змінним, указаним у виклику read (readln). Якщо сталих менше, ніж змінних у виклику, то виконання продовжується, тобто внутрішній буфер спорожнюється та починається чекання нових символів із клавіатури. Коли в черговому рядку проаналізовано останню сталу, поточним стає наступний за нею символ у внутрішньому буфері. Виконання процедури read на цьому закінчується. За наступного виконання процедури читання нові символи будуть додаватися до змісту внутрішнього буфера, але пошук і аналіз сталих почнеться від поточного символу буфера, що залишився від попереднього виклику read. Особливість процедури readln полягає в тім, що після аналізу останньої сталої решта символів у внутрішньому буфері пропускаються разом із найближчим eol, тобто фактично буфер скидається. За виклику функції EOF аналізується внутрішній буфер клавіатури. Якщо він порожній, то виконання програми зупиняється до найближчого натискання клавіш. Тоді символи з буфера клавіатури переносяться у внутрішній до появи Enter. За наявності символів у внутрішньому буфері аналізується перший, поточний символ. Якщо він відповідає сполученню клавіш Ctrl-Z, яким задається кінець файла на клавіатурі, то з виклику eof повертається значення true. За іншого першого символу, тобто при натисканні клавіші, відмінної від Ctrl-Z, повертається false. Приклад. Нехай діє означення var V : integer, а клавіші не натискалися до початку виконання такого фрагмента програми: V := 0; while not eof do begin write( 'Задайте ціле число>' ); read( V ) end; writeln ( 'V=', V:1 ) При виконанні eof комп'ютер чекає натискань на клавіші. Запрошення до введення числа на екрані ще не з’ явилося. Якщо натиснути Ctrl-Z та Enter, то з виклику eof повертається false, і виконання фрагмента закінчується друкуванням тексту V=0. За натискань цифрових клавіш цифри відображаються на екрані й накопичуються у внутрішньому буфері клавіатури. Після натискання на Enter виконання eof закінчується й повертається значення false. Після цього, тобто лише після набору на клавіатурі першої сталої (!) виконується тіло циклу і з'являється запрошення 'input number>'. При виконанні read лише аналізуються символи, накопичені у внутрішньому буфері за виконання виклику eof. Якщо вони утворюють сталу, то відповідне значення присвоюється змінній V, після чого повторюється виклик eof тощо. Отже, введення символів із клавіатури в такому циклі відбувається за викликів eof, а не read! Таким чином, щоб запрошення друкувалося до початку введення першої сталої, варто перед циклом додати виклик write('input number>'). Ще раз повернемося до уживання процедури readln замість read. Якщо при виконанні наведеного циклу за черговою сталою після пропуску випадково набрати непорожні символи, що не задають сталу, то вони залишаться у внутрішньому буфері. Далі з виклику eof повернеться false, і аналіз цих символів за виконання read призведе до аварійного завершення програми. Якщо ж замість read записати readln, то після обробки сталої ці символи пропускаються, тому що набираються перед Enter, і програма виконується нормально. 15.4. Тип безтипових файлів Розглянемо програму посимвольного копіювання файлів: program StupidCopy; var f, g : file of char; c : char; s : string; begin writeln('Задайте ім'я початкового файла'); readln(s); assign(f, s); writeln('Задайте ім'я цільового файла'); readln(s); assign(g, s); reset(f); rewrite(g); while not eof(f) do begin read(f, c); write(g, c); end; close(f); close(g); end. Здається, що при виконанні цієї простенької програмки все гаразд, оскільки за рахунок використання буферів фізичні файли читаються-записуються порціями по кілька блоків, пристрої при цьому працюють найкращим чином, а переміщення інформації відбуваються головним чином усередині оперативної пам’ яті, тобто швидко. Спробуйте запустити її на виконання, указавши вхідним файл розміром у кілька сотень кілобайтів – виконання займе секунди й десятки секунд. Напрошується висновок, що при її виконанні дещо здійснюється не найкращим чином. Розглянемо один із способів прискорення роботи з файлами. Система Турбо Паскаль дозволяє створити додатковий власний буфер і власноруч описати його застосування. Це виявляється набагато ефективнішим від використання буферів, що забезпечуються системою. А реалізується це за допомогою безтипових файлів. Тип безтипових файлів задається словом file. Файлову змінну цього типу, як і всіх інших файлових типів, треба спочатку зв’ язати з фізичним файлом і відкрити, установивши в початковий стан для читання чи запису. Процедури відкривання RESET і REWRITE тут мають по 2 параметри. Крім імені файлової змінної, у їх виклику вказується розмір "зовнішнього буфера" в байтах. Цей буфер ще називається блоком і явно в Паскаль-програмі не позначається. Через нього дані копіюються з фізичного файла до "внутрішнього буфера". Розмір блока може бути в межах від 1 до 65535. Як не дивно, найкраще встановити його рівним 1: ReSet(f, 1) або ReWrite(g, 1). Чому саме так, ми скажемо далі. Уся подальша обробка безтипового файла описується зовсім іншими засобами. Читання безтипових файлів задає процедура BLOCKREAD із чотирма параметрами. Усі вони, крім третього – параметри-змінні. У виклику процедури першим аргументом є ім’ я файлової змінної, другий задає місце в пам’ яті, з якого починається "внутрішній буфер", третій – кількість блоків, які треба прочитати з файла, а в четвертому, типу Word, повертається кількість блоків, яка насправді читається за виконання виклику. Наприклад, за означеннями var f : file; inbuf : array[1..100] of char; blsz : Longint; numbl, numblre : Longint та операторами й викликами blsz:=4; numbl:=25; reset(f, blsz); blockread(f, inbuf, numbl, numblre) розмір блока встановлюється рівним 4 байти, і 25 таких блоків треба прочитати з початку файла. Якщо розмір файла насправді не менше 4*25=100 байтів, і ніяких помилок при читанні не було, то значенням змінної numblre також буде 25. Після читання масив inbuf буде заповнений до кінця, і треба буде обробити його залежно від конкретної задачі. Крім того, при виконанні наступного виклику цієї процедури файл f буде читатися зі 101-го байта. Отже, для безтипових файлів поняття "доступний елемент" заміняється на "доступний байт". Можливо, в файлі менше 100 байтів або при читанні щось трапилося, і насправді прочитано менше, ніж указані 25 блоків. Тоді значення змінної numblre буде не рівним 25. Після виклику можна задати перевірку numblre=numbl і відповідні дії в разі нерівності. Якщо задати читання кількості блоків, меншої від 25, то масив inbuf буде заповнений не до кінця, а якщо більшої – то заповниться масив inbuf і відповідна кількість змінних, розташованих за ним у пам’ яті програми. Оскільки змінні розташовуються там у порядку означення, першими "жертвами" в даному разі стануть змінні blsz, numbl, numblre. Вони мають тип Longint і займають по 4 байти, тому за виконання blockread(f, inbuf, 26, numblre) буде зіпсована лише перша з них, за blockread(f, inbuf, 27, numblre) – перші дві тощо. Отже, треба бути особливо уважним при записі виклику. Якщо блок, або "зовнішній буфер" не заповнюється до кінця, то кількість блоків, реально прочитаних, буде меншою від заданої кількості. Таким чином, для запобігання неприємностей треба забезпечити, щоб розмір файла ділився на розмір блока. Оскільки розмір блока насправді не впливає на швидкість читання, найкраще надавати йому значення 1. Тоді проблем не буде за будь-якого розміру файла.
Назва: Ідея буферизації Дата публікації: 2005-02-22 (667 прочитано) |