Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Программирование для начинающих #21


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Программирование для начинающих

Выпуск 21

18 AUG 2001

 
 
 
Ведущий рассылки: Вячеслав Мацнев
e-mail: stac@stacmv.net
Здравствуйте, уважаемые подписчики!

В этом выпуске читайте:

Пришло время нам снова встретиться, и вот я здесь, приветствую всех вас!

ОТСЕБЯТИНА .::. После перерыва...

После перерыва в наших занятиях мы вновь начинаем... Ой, извините, продолжаем, конечно. ...изучать программирование.

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

Идеи, которые у меня сейчас имеются по этому поводу, я изложу ниже в этом выпуске.

ИНФОРМАЦИЯ .::. для новых подписчиков

Если вы подписались на рассылку недавно, особенно, если это первый выпуск, который вы видите, то вам будет интересно узнать, что все ранее вышедшие выпуски можно всегда взять в архиве на сайте рассылки.

В рамках рассылки у нас действует одноименный курс (или рассылка выходит в рамках курса?), студенты которого активно изучают программирование (чтение рассылки это, понятно, пассивное изучение). Этому способствуют домашние задания, которые студентам необходимо выполнять. Информацию о том как стать студентом этого курса можно найти на сайте рассылки или в ранних выпусках рассылки.

Сегодня мы будем говорить о файлах. Я не буду объяснять, что это такое и где находится. Вопросы, связанные с файлами уже освещались в рассылке. Рекомендую вам раздобыть в архиве выпуски 6.1, 6.2 и 9 и внимательно прочитать, что там говорилось о файлах. Эта информация может оказаться очень полезной.

Несколько полезных ссылок:

http://stacmv.nm.ruсайт рассылки"Программирование для начинающих"
http://stacmv.nm.ru/arch_idx.htmlархив рассылки
http://stacmv.nm.ru/edu_idx.htmlинформация о курсе "Программирование для начинающих"

ТЕОРИЯ .::. Работа с файлами

Файлы - это единственное средство долговременного хранения информации на компьютере.

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

Для многих программ это (потеря данных) является нежелательным. Представьте текстовый редактор, в котором вы можете набрать текст и распечатать его. При выходе из текстового редактора набранный текст будет утерян.

Разумнее, конечно, сохранить текст в файле на каком-нибудь носителе. Чтобы в будущем его можно было загрузить в текстовый редактор и использовать.

Таким образом, мы видим, что одно из применений файлов (с точки зрения программиста) это сохранение результатов работы программы для будущего использования.

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

Операционная система предоставляет доступ к файлам, используя довольно хитрую систему. Так для каждого файла с которым ведется работа в памяти создаются специальные управляющие структуры (такие как FCB и другие).

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

Обычно программисту (особенно прикладному) не приходится об этом задумываться, т.к. все хитроумные манипуляции операционной системы от него скрыты и являются для него как бы прозрачными.

Зачем я вообще стал об этом говорить? Дело в том, что файл нельзя просто взять и прочесть, даже если известно его имя и все такое. Точнее это нельзя сделать средствами языка программирования высокого уровня, к которым относится и Бейсик.

В таких языках принято файл сначала открывать. Открытие файла это совсем не то, что понимается под словами "Открыть файл" в том же текстовом редакторе. Реально там происходит открытие файла, его чтение и закрытие.

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

Для компьютерного файла процедура открытия сводится к созданию для этого файла специальных структур в памяти, типа с буфером и пр., прописке его во всяких таблицах, типа SFT.

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

Такой подход является общим для большинства языков программирования. Например, в Си идентификатор файла это указатель на структуру FILE, в Паскале это переменная типа file, в Бейсике это просто число (которое воспринимается как идентификатор файла соответствующими процедурами и функциями).

После того, как файл открыт, можно его читать и писать в него. Для этого в каждом языке есть соответствующие средства.

Файл может быть открыт для чтения, для записи, для чтения и записи одновременно. Доступ к файлу может быть как последовательный, так и произвольный.

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

При работе с файлом произвольного доступа мы можем читать произвольное место файла. При этом файл поделен (логически) на записи одинакового размера. Обычно эти записи соответствуют переменным какого-то одного типа и такие файлы называют типизированными.

Появляется понятие "текущая запись" и существует указатель на эту запись, значение которого мы можем анализировать и устанавливать.

Перемещаясь по файлу мы можем считать нужную запись или записать на ее место новые данные.

При последовательном доступе запись ведется, понятно, последовательно либо "с чистого листа" (при этом предыдущее содержимое файла, если такое было, теряется), либо в режиме добавления. Способ записи определяется при открытии файла.

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

В DOS есть несколько команд (FILES, FCB - см. файл CONFIG.TXT в каталоге WINDOWS), которые управляют этой памятью, т.е. определяют ее размер.

Поэтому, неиспользуемые файлы нужно закрывать. Это также следует делать в целях безопасности. Чтение и запись в файлы буферизуется, т.е. информация пишется не в файл, а в соответствующий буфер (это делается для повышения производительности, т.к. работа с памятью, где находится буфер, намного быстрее, чем с внешними накопителями). Запись непосредственно в файл производится операционной системой, когда буфер заполнен. Однако, если еще до заполнения буфера произойдет сбой (программный или просто электричество отрубят), то содержимое буфера будет потеряно. При закрытии файла, содержимое буфера записывается в файл.

БЕЙСИК .::. Основные приемы работы с файлами

Теперь, после небольшого теоретического введения рассмотрим основные, наиболее часто используемые приемы (и соответствующие команды)работы с файлами на Бейсике.

Как уже отмечалось, прежде всего файл нужно открыть. Давайте проделаем это с файлом autoexec.bat.

Для открытия файла используется оператор OPEN (это "открыть" по-английски). Он имеет следующий формат:

OPEN файл$ [FOR режим] [ACCESS доступ] [закр] AS [#]номер_файла% [LEN=д_зап%]

файл$ Имя файла или устройства. Имя файла может включать устройство и путь.

режим Один из следующих режимов файла: APPEND, BINARY, INPUT, OUTPUT или RANDOM.

доступ При работе в сети указывает, открыт ли файл для READ (чтения), WRITE (записи) или READ WRITE (чтения-записи).

закр Указывает, как файл закрыт для сетевого доступа: SHARED (общий), LOCK READ (закрыт для чтения), LOCK WRITE (закрыт для записи), LOCK READ WRITE (закрыт для чтения-записи).

номер_файла% Номер в пределах от 1 до 255, идентифицирующий открытый файл.

д_зап% Для файлов прямого доступа - длина записи (по умолчанию 128 байт). Для последовательных файлов - число буферированных символов (по умолчанию 512 байт).

Это выдержка из хелпа к QBASIC'у. Пока можно забыть про сетевые примочки и про длину записи. Что остается?

файл$ - это строка (STRING), содержащая имя файла, например, "C:\autoexec.bat".

Далее, режим это одно из следующих ключевых слов:

  • APPEND указывает, что файл должен быть открыт для последовательного вывода, и устанавливает указатель файла в конец файла. В этом случае операторы, записывающие в файл, дополняют его.
  • BINARY указывает бинарный режим файла. В бинарном режиме можно считывать или записывать информацию в позицию любого байта.
  • INPUT указывает, что файл открыт для последовательного ввода.
  • OUTPUT указывает, что файл открыт для последовательного вывода.
  • RANDOM указывает, что файл открыт в режиме прямого доступа к файлу. Этот режим устанавливается по умолчанию.

Откроем файл для чтения.

OPEN "C:\autoexec.bat" FOR INPUT AS #1

По-русски (при дословном переводе) этот оператор звучит так: "Открыть C:\autoexec.bat для ввода как №1".

В дальнейшем мы будем обращаться к этому файлу не по имени, а по номеру (#1).

Давайте пока закроем файл. Для этого служит команда CLOSE ("закрыть").

CLOSE номер_файла% [, [номер_файла%] ...]

CLOSE закрывает указанные файлы. Если не указано ни одного номера файла, закрываются все файлы. Я рекомендую всегда указывать номера файлов. Потому что, будет нехорошо, если вы по ошибке (по привычке) закроете все файлы, когда этого делать не следует.

Итак,

CLOSE #1

Можно прочесть файл и, например, распечатать его на экране. Как ни странно, для ввода данных из файла используются те же операторы, что и для ввода с клавиатуры.

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

Для ввода используется операторы INPUT и LINE INPUT. Второй читает строку длиной до 255 символов. Признаком конца строки служат символы возврата каретки (CHR$(13)) и перевода строки(CHR$(10)), вместе или по отдельности. При вводе из файла у этих операторов первым аргументом должен быть номер файла.

Оператор LINE INPUT удобно использовать при работе с текстовыми файлами. Мы знаем, что autoexec.bat это текстовый файл, поэтому будем использовать LINE INPUT.

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

На помощь приходит функция EOF(N). Она возвращает значение "истина", если достигнут конец файла, номер N.

Вот такая простая программа читает файл и выводит его на экран. Она применима для небольших файлов: не более 23 строк, не более 79 символов в строке.

REM Вывод небольшого текстового файла на экран.
CONST F = "C:\autoexec.bat"
DIM s$

OPEN F FOR INPUT AS #1 CLS PRINT "Файл : "; F DO UNTIL EOF(1) LINE INPUT #1, s$ PRINT s$ LOOP CLOSE #1

Здесь используется цикл с предусловием на тот случай, если файл окажется пустым.

Что касается больших файлов, то они тоже будут выводиться этой программой, но при этом может не соблюдаться форматирование текста, если строка не поместится на на экране, да и увидите вы только последние строки, остальные "прокрутятся" с большой скоростью. Попробуйте.

Создание текстовых файлов также очень просто. Для вывода в файл используется оператор PRINT. Как его использовать, вы прекрасно знаете. Надо лишь, чтобы первым аргументом был номер файла.

PRINT выводит в файл такие строки, которые потом удобно читать оператором LINE INPUT (т.е. обычные строки, завершенные CHR$(13)+CHR$(10)). Если вы, конечно, не станете завершать оператор символом ";" (точка с запятой). Словом оператор PRINT ведет себя совершенно одинаково как с экраном, так и с файлом.

Модифицируем предыдущую программу:

REM Вывод небольшого текстового файла на экран
REM и копирование его в другой файл.
CONST INF = "C:\autoexec.bat"
CONST OUTF = "C:\temp.tmp"
DIM s$

OPEN INF FOR INPUT AS #1 OPEN OUTF FOR OUTPUT AS #2 CLS PRINT "COPY : "; INF; " ---> "; OUTF DO UNTIL EOF(1) LINE INPUT #1, s$ PRINT #2, s$ PRINT s$ LOOP CLOSE #1, #2

Здесь мы открываем два файла, один на чтение, другой на запись.

Кстати, если программа открывает много файлов, то с их номерами можно запутаться. Т.е. можно попытаться открыть файл под номером, который уже используется. Чтобы такого не случилось, нужно использовать функцию FREEFILE. Она возвращает номер следующего доступного неиспользуемого файла.

Присвойте этот номер переменной и затем ее используйте.

Теперь вы знаете как копировать файлы. Текстовые.

Интересный момент здесь присутствует. Есть такой символ, называется eof (end of file - конец файла). Его код 1Ah (26). Он может стоять в конце файла, а может и не стоять (в большинстве случаев). Даже если символа eof в конце нет, функция EOF() его(конец файла) определит правильно.

Однако, если символ eof каким-либо образом окажется в середине файла, то функция EOF() его опознает и вернет в вызывающую программу значение "истина".

Т.е. наша программа и многие команды DOS, такие как TYPE и COPY /A, прочтут файл не до конца, а до этого символа eof. Попробуйте.

Записать символ eof в файл можно с помощью шестнадцатеричного редактора или программно с помощью того же PRINT (напечатайте CHR$(26)).

Этому явлению можно найти массу интересных применений (дайте поработать своему воображению). Правда более или менее серьезные программы (типа просмотрщика DOS Navigator'а) относятся к этому несколько пренебрежительно, т.е. читают весь файл, сколько бы концов он не содержал.

Нашу программу тоже можно научить этому. Подумайте как.

...

Подумали? Думайте, не стесняйтесь, никто не видит.

...

Ок. Если мы определим размер файла в байтах, а затем при чтении файла будем считать прочитанные байты. ... и читать файл до конца, даже если какие-нибудь хитрецы вроде нас напичкают файл eof'ами под завязку.

Определить размер нам поможет функция LOF(N), где N - номер файла.

Конечно только определить размер файла мало. Потому что, даже если мы узнаем, что встреченный eof это не настоящий конец файла и продолжим чтение, LINE INPUT не проскочит eof, а прочтет его. Это вызовет ошибку "Ошибка: введен конец файла".

Поэтому будем работать с файлом не как с текстовым, а как с двоичным. Откроем его как BINARY.

Правда теперь LINE INPUT работать не будет ("Не тот режим"). Для двоичного режима есть свои операторы чтения и записи. В этом режиме доступ к файлу произвольный (или прямой, что одно и то же), а запись эквивалентна байту (т.е. длина записи равна 1 байт).

Читать двоичный файл можно оператором GET. Его формат:

GET номер_файла% [,[номер_записи][, переменная]]

Номер записи в данном случае это номер байта от начала файла (первый байт имеет номер 1), а переменная это буфер определенного размера. Эта переменная может быть любого типа, кроме STRING, т.к. размер последнего не определен.

Модифицируем нашу программу:

REM Программа 21.1
REM Вывод небольшого текстового файла на экран
REM и копирование его в другой файл.
CONST INF = "C:\autoexec.bat"
CONST OUTF = "C:\temp.tmp"
DIM s$
DIM ch AS STRING * 1 'буфер размером 1 байт

OPEN INF FOR BINARY AS #1 L = LOF(1) i = 1 OPEN OUTF FOR OUTPUT AS #2 CLS PRINT "COPY : "; INF; " ---> "; OUTF DO GET #1, i, ch i = i + 1 PRINT #2, ch; PRINT ch; LOOP WHILE i < L CLOSE #1, #2

Хорошо. Разобрались. Вышеприведенная программа имеет ряд недостатков, но их устранение будете производить сами (при выполнении ДЗ).

Следующий момент, который интересует некоторых подписчиков, это как объединить несколько файлов в один.

Как Вы понимаете это довольно просто. Нужно прочитать несколько файлов, как мы делали выше, а писать все в один и тот же.

Вот пример такой программы:

REM Программа 21.2
REM В качестве параметров командной строки принимает список
REM из N файлов. Копирует первые N-1 файлов в последний.
REM Имя файла назначения не может совпадать с именем файла
REM источника.
DECLARE FUNCTION getlastword$ (s$)
DECLARE FUNCTION getfirstword$ (s$)
DECLARE SUB copyfile (inf AS INTEGER, outf AS INTEGER)

DIM count AS INTEGER 'счетчик скопированных файлов DIM cmd$ 'копия командной строки DIM inf AS INTEGER 'идентификатор файла источника DIM outf AS INTEGER 'идентификатор файла назначения DIM infn$ 'имя файла источника DIM outfn$ 'имя файла назначения

cmd$ = COMMAND$ IF INSTR(cmd$, " ") = 0 THEN PRINT "Должно быть минимум два параметра!" END END IF count = 0 outfn$ = getlastword$(cmd$) 'имя файла назначения = последнее слово в 'командной строке. outf = FREEFILE OPEN outfn$ FOR OUTPUT AS outf infn$ = getfirstword$(cmd$) 'имя файла источника = первое слово в 'командной строке. DO inf = FREEFILE OPEN infn$ FOR BINARY AS inf copyfile inf, outf CLOSE inf count = count + 1 infn$ = getfirstword(cmd$) LOOP UNTIL infn$ = "" 'пока есть файлы источники CLOSE outf PRINT "Скопировано "; count; " файлов."

SUB copyfile (inf AS INTEGER, outf AS INTEGER) 'Копирует открытый for BINARY файл #inf в открытый 'for OUTPUT файл #outf DIM l AS LONG, i AS INTEGER DIM ch AS STRING * 1 'буфер

l = LOF(inf) 'длина файла источника в байтах i = 0 'счетчик прочитанных байт DO i = i + 1 GET inf, i, ch PRINT #outf, ch; LOOP WHILE i < l END SUB

FUNCTION getfirstword$ (s$) 'Возвращает первое слово из строки, удаляя его из s$ 'или пустую строку, если s$="". 's$ должна передаваться по ссылке. DIM i%, j% DIM gfw$

IF LEN(s$) = 0 THEN getfirstword$ = "" EXIT FUNCTION END IF i% = INSTR(s$, " ") IF i% = 0 THEN gfw$ = s$ s$ = "" ELSE gfw$ = LEFT$(s$, i% - 1) s$ = MID$(s$, i% + 1) END IF getfirstword$ = gfw$ END FUNCTION

FUNCTION getlastword$ (s$) 'Возвращает последнее слово из строки, удаляя его из s$. 's$ должна передаваться по ссылке. DIM i%, j% DIM ch$,glw$

i% = LEN(s$) j% = 0 DO ch$ = MID$(s$, i%, 1) i% = i% - 1 j% = j% + 1 LOOP UNTIL ch$ = " " glw$ = RIGHT$(s$, j% - 1) s$ = MID$(s$, 1, i%) getlastword$ = glw$ END FUNCTION

Здесь есть один новый для вас момент - функция COMMAND$. Она возвращает параметры командной строки. Т.е. если вашу программу CONCAT.EXE запустили следующей командой:

CONCAT.EXE c:\autoexec.bat c:\config.sys c:\temp.tmp

то COMMAND$ вернет строку "C:\AUTOEXEC.BAT C:\CONFIG.SYS C:\TEMP.TMP" (именно так, в верхнем регистре).

Постарайтесь разобраться в этой программе. У кого не получится, пишите, спрашивайте, что непонятно.

Закончим на сегодня возню с файлами, продолжим ее в следующий раз. Разберитесь со всем сегодняшним материалом, на его основе будут несколько задач в ДЗ basic/3.

ЗАКЛЮЧЕНИЕ .::. Фонтан идей

Попробуйте найти и исправить ошибки в опубликованных в этом выпуске программах. Они в них есть и, видимо, в немалых количествах. Словом распространять такие программы нельзя (даже если приделать к ним удобный пользовательский интерфейс).

Мне не хочется сейчас говорить, почему так получается. Мы будем обсуждать подобные вопросы, когда будем работать над проектами. Тогда я буду очень суровым, буду заставлять вас выверять каждую строчку кода и подписываться под ней.

Это сейчас я добрый. Проверяю ваши ДЗ, нахожу очевидные ошибки, объясняю. Расстраиваюсь, когда вы элементарно не проверяете свои программы на работоспособность.

Но ничего. Появилось тут у меня несколько идей, как обычно. Пару-тройку из них надо попытаться реализовать.

Раньше я думал, что мы напишем программу для передачи всяких данных в текстовом виде через Инет. Подумал, посмотрел, как это можно сделать. Оказалось немного сложновато для начала.

Для начала надо написать что-нибудь попроще. Например, программу для тестирования. Во втором ДЗ даже появилась задача из этой оперы с которой все благополучно справлялись.

Стал я думать про эту тестирующую программу. Думал-думал. Прикинул, что она должна из себя представлять. В итоге это вылилось в проект "Контрольная работа". Эта тестирующая программа теперь является пакетом из четырех программ. Однако врядли мы напишем их сейчас, ведь программа для передачи теперь всего лишь одна из подсистем (кодек) тестирующей программы.

Надо было придумать что-нибудь попроще. Еще попроще.

Идей на этот счет не было, да и армия наша Российская, где я имел удовольствие проводить время этим летом, к подобным идеям не располагала.

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

Как управлять таким проектом? Не знаю, никогда не был менеджером проекта с более чем тремя участниками. Ну это, думаю, ладно, в случае чего можно будет у Надежды подсказки спросить. А может еще кто-нибудь советом иль еще чем поможет, народу-то среди подписчиков высококвалифицированного полно.

Чудненько. Раз все прекрасно, чего же эт я не весел. Да подумал о том, как работа над проектами будет технически реализована.

После того как мы выясним, чего мы будем писать, начнется подбор членов команды разработчиков. Допустим, команда набрана, и сколько бы человек в ней не было, каждый ухватился за какую-то задачу и приступил к ее решению.

Через определенное время разные товарищи начнут подсыпать куски доброго кода, положим мне. А я займусь их дистрибуцией среди разработчиков и, допустим, заинтересованной общественности, то бишь подписчиков.

Мало помалу, у нас соберется этакая коллекция отдельных кусков программы, скажем функций. Надо будет их все в одну программу собирать.

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

Словом, нам нужен инструмент, способный решить все эти проблемы и кучу других.

К сожалению, я никогда не работал со всякими системами контроля версий и т.п. И поэтому знаю о них, может, меньше вас.

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

Я прошу подумать на эту тему всех подписчиков, потому что есть большая вероятность, что нам придется писать для себя собственную систему управления проектами.

Я даже, пожалуй, начну собирать команду для первого нашего проекта. Кто хочет принять участие, пишите о своем желании по адресу mailto:stac@stacmv.net?Subject=FirstProjectTeam

Единственным требованием, кроме, конечно, желания поучаствовать, является способность хотя бы раз в неделю связаться со мной по email.

На этом я с вами прощаюсь, друзья.

И да прибудет с нами Сила! =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

С уважением,
Вячеслав Stac Мацнев mailto:stac@stacmv.net
18.08.01.



http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться Рейтингуется SpyLog

В избранное