Сторінка
1

Паскаль: буферизація введення-виведення

1. Ідея буферизації Дівчині Галі треба кілька склянок води, щоб полити квіти на вікні. Але вона чомусь іде до колодязя з двома здоровенними відрами. Шлях туди не зовсім короткий, відра важкі, але принісши їх, Галя не піде до колодязя за наступними склянками, аж поки не вичерпає з відер усю воду. Такі самі міркування лежать в основі організації обміну даних між фізичними файлами та змінними програми. Фізичний файл можна порівняти з колодязєм. Добування води з нього вимагає багато часу, і вигідно це робити не надто часто та не зовсім малими дозами. У ролі відра під час виконання програми виступає буфер – спеціальна ділянка пам’ яті програми, яка надається кожній файловій змінній при її зв’ язуванні. Спочатку розглянемо фізичні файли на диску. Дискова пам’ ять розбивається на блоки – ділянки фіксованого розміру (найчастіше, по 512 байтів). Пристрої обміну (дисководи) створено так, що саме блоками дані копіюються на диск або з диску. Блок є одиницею фізичного обміну між диском та оперативною пам’ яттю. Коли виконується виклик підпрограми читання, в буфер копіюється цілий блок або кілька блоків даних із файла, і до змінних значення потрапляють не з файла, а з буфера. За наступних читань жодних переміщень даних між диском та оперативною пам’ яттю немає, а значення беруться з буфера. Розмір буфера визначається операційною системою. Як правило, це 2, 8, 16 або навіть більше блоків. З кожним буфером зв’ язана додаткова змінна, яка вказує на його поточний елемент. Саме його значення копіюється при читанні, а поточним стає наступний за ним. Можна вважати, що поточний елемент в буфері виступає представником доступного елемента файла. Якщо весь буфер прочитано, то за чергової спроби читання наступні кілька блоків файла копіюються в буфер. Підкреслимо, що буфер, як і вказівник поточного елемента, у Паскаль-програмі явно не означається і не використовується. Буфер можна розглядати як своєрідне вікно, крізь яке з програми видно файл. Якщо фізичний файл менший буфера за розміром, тобто весь файл "уміщається у вікні", то всі читання з нього вимагають лише одного звернення до файла. При записі у файл дані записуються в буфер до його заповнення, і фізичне копіювання на диск, або скидання буфера, виконується лише за спроби додати дані в заповнений буфер. Після скидання буфер заповнюється з початку. Незаповнений буфер скидається у файл по закінченні виконання програми. Зауважимо, що організація буферів текстів у Турбо Паскалі має додаткові особливості, які розглядаються далі. Наприклад, про тексти не можна стверджувати, що незаповнений буфер скидається по закінченні програми. Але буфери файлів усіх типів скидаються при виконанні процедури закриття close. Ось чому варто роботу з файлом описувати в "дужках", утворених викликами процедур відкривання та закривання. Використання буфера дозволяє "вбити двох зайців". По-перше, враховуються особливості зовнішніх пристроїв, які вимагають обмінювати дані великими порціями (по кілька блоків). По-друге, за більшості викликів підпрограм читання та запису відбувається лише переміщення значень між різними ділянками оперативної пам’ яті, а це набагато швидше від обмінів із диском. Отже, застосування буферів, або буферизація, як правило, зменшує кількість фізичних переміщень даних між зовнішніми носіями та оперативною пам’ яттю і прискорює введення-виведення.

2. Буферизація текстів З текстом зв’ язано не один, а два буфери. Перший, зовнішній, обробляється згідно написаного в попередньому підрозділі. Робота з другим, внутрішнім, ведеться інакше. При читанні дані копіюються з тексту в зовнішній буфер, а звідти частина їх копіюється у внутрішній. Яка саме частина, залежить від розміру внутрішнього буфера. При читанні символи тексту насправді беруться із внутрішнього буфера, а коли він вичерпується, то в нього копіюється наступна частина зовнішнього буфера (можливо, зі зверненням до фізичного файла), і читання продовжується. Розмір внутрішнього буфера текстів установлюється в системі програмування у 128 байтів. Програміст має можливість змінити його в межах від 1 до 65536 байтів викликом процедури SETTEXTBUF вигляду

settextbuf(f, Buf, Bufsize) або

settextbuf(f, Buf), де f – ім’ я файлової змінної типу text, Buf – ім’ я змінної, тип якої несуттєвий, а Bufsize – вираз цілого типу зі значенням у межах 1 65535. Такий виклик слід записувати після зв’ язування файла перед установленням його в початковий стан (читання чи запису). Змінна Buf використовується як внутрішній буфер, тому доцільно, щоб її довжина була кратною довжині блоку. Якщо розмір буфера (у байтах) Bufsize у виклику не вказано, то він визначається довжиною змінної Buf. Якщо Bufsize указано і менше довжини змінної Buf, то воно задає довжину буфера в межах змінної Buf. Але якщо Bufsize більше довжини Buf, то змінні, розташовані в пам’ яті безпосередньо за Buf, використовуються під буфер, і це може призвести до непередбачених наслідків. Приклад 15.1. Розглянемо програму program GreatBufferManager; var f : text; Hugebuf : array[1 2]of char; x, y : char; s : string[4]; begin assign(f, 'huge.dat'); settextbuf(f, hugebuf, 4); x:='x'; y:='y'; reset(f); readln(f, s); writeln('x=', x, '; y=', y);

readln; end. Якщо першим рядком тексту в файлі huge.dat є 'qwer', то за виконання цієї програми на екрані з’ явиться зовсім не очікуване x=x; y=y, а на перший погляд досить дивне x=e; y=r. Справа в тім, що змінні x і y фізично розташовані безпосередньо за масивом Hugebuf, і читання чотирьох символів рядка файла в цей буфер призводить до заповнення не тільки масиву, а й змінних за ним. Якщо зробити рядок у файлі трошки довшим, то, запевняємо читача, результати будуть ще несподіванішими. Але не захоплюйтесь, це може стати небезпечним для програми GreatBufferManager. При виведенні в текст символи накопичуються у внутрішньому буфері, який скидається в зовнішній буфер у разі заповнення або виконання процедури writeln чи close. Можна також задати примусове скидання внутрішнього буфера тексту f викликом процедури FLUSH(f). Його варто записувати для періодичного виконання, а також після всіх виведень у файл. Підкреслимо, що за виклику процедури flush лише скидається внутрішній буфер у зовнішній. Скидання зовнішнього буфера при цьому відбувається лише у випадку його заповнення. Якщо в кінці роботи з файлом не указати викликів flush чи close, то зміст внутрішнього буфера так і не потрапить у зовнішній буфер і у файл. Приклад 2. Здається, наступна програма задає копіювання текстів: program wrongcpy; var f, g : text; c : char; begin assign(f, .); assign(g, .); reset(f); rewrite(g); while not eof(f) do begin read(f, c); write(g, c) end; {тут не вистачає close(g) ! Хоча й close(f) не завадить .} end. Спробуйте цю програму запустити, і побачите, що якщо початковий файл – пісня, то файл-"копія" – теж пісня, але недоспівана. А все тому, що "кінець пісні" так і залишається у внутрішньому буфері. Змінну під внутрішній буфер варто означати глобальною в програмі. Якщо означити та зв’ язати файлову змінну в програмі, а її внутрішній буфер означити у підпрограмі, то по закінченні виклику підпрограми файлова змінна буде доступною, а її буфер – ні. Спроба скидання з такого буфера по закінченні програми може призвести до непередбачених наслідків. Але якщо вся робота з файлом, від assign до close, описана в підпрограмі, то й буфер цілком природньо означити в ній же. Приклад 3. Розглянемо програму з процедурою spoilbuf, тобто "зіпсувати буфер", за виклику якої змінюється буфер, що залишається в локальній пам’ яті після закінчення попередньої процедури fillbuf.

Перейти на сторінку номер:
 1  2  3  4 


Інші реферати на тему «Інформатика»: