Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Pol_Grem_-_ANSI_Common_Lisp_High_tech_-_2012.pdf
Скачиваний:
30
Добавлен:
12.03.2016
Размер:
4.85 Mб
Скачать

7

Ввод и вывод

Common Lisp имеет­ бога­тые­ возмож­но­сти­ для осуще­ст­в­ле­ния­ ввода­-вы­ вода­. Для ввода,­ наря­ду­ с обычны­ми­ инст­ру­мен­та­ми­ для чтения­ симво­­ лов, у нас есть read, кото­рый­ включа­ет­ в себя­ полно­цен­ный­ парсер­. Для выво­да,­ вместе­ с привыч­ны­ми­ средст­ва­ми­ для запи­си­ симво­лов,­ мы по­ луча­ем­ format, кото­рый­ сам по себе­ явля­ет­ся­ неболь­шим­ языком­. В этой главе­ вводят­ся­ все основ­ные­ поня­тия­ ввода­-выво­да­.

Суще­ст­ву­ет­ два вида­ пото­ков:­ пото­ки­ знаков­ и бинар­ные­ пото­ки­. В этой главе­ рассмот­ре­ны­ лишь пото­ки­ знаков;­ бинар­ные­ пото­ки­ будут­ описа­­ ны в разде­ле­ 14.2.

7.1. Потоки

Пото­ки ­– это объек­ты­ Лиспа,­ представ­ляю­щие­ собой­ источ­ни­ки­ и/или прием­ни­ки­ для пере­да­чи­ симво­лов­. Чтобы­ прочи­тать­ или запи­сать­ файл, необ­хо­ди­мо­ открыть­ соот­вет­ст­вую­щий­ поток­. Одна­ко­ поток ­– это не то же самое,­ что файл. Когда­ вы читае­те­ или печа­тае­те­ что-либо­ в toplevel,­ вы также­ исполь­зуе­те­ поток­. Созда­вая­ пото­ки,­ вы даже­ може­те­ читать­ из строк или писать­ в них.

По умолча­нию­ для ввода­ исполь­зу­ет­ся­ поток­ *standard-input*, для вы­во­ да – *standard-output*. Перво­на­чаль­но­ они, как прави­ло,­ ссыла­ют­ся­ на один и тот же поток,­ соот­вет­ст­вую­щий­ toplevel.

С функция­ми­ read и format мы уже знако­мы­. Ранее­ мы пользо­ва­лись­ ими для чтения­ и печа­ти­ в toplevel. Функция­ read имеет­ необя­за­тель­ный­ ар­ гумент,­ кото­рый­ опре­де­ля­ет­ входной­ поток­ и по умолча­нию­ уста­нов­лен­ в *standard-input*. Первый­ аргу­мент­ функции­ format также­ может­ быть пото­ком­. Ранее­ мы вызы­ва­ли­ format с первым­ аргу­мен­том­ t, кото­рый­ со­ ответ­ст­ву­ет­ пото­ку­ *standard-output*. Таким­ обра­­зом, до этого­ момен­та­ мы наблю­да­ли­ лишь пове­де­ние­ этих функций­ при исполь­зо­ва­нии­ аргу­­

7.1. Потоки

131

ментов­ по умолча­нию­. Одна­ко­ те же опера­ции­ мы можем­ совер­шать­ и с любы­­ми други­ми­ пото­ка­ми­.

Путь (pathname) – это пере­но­си­мый­ способ­ опре­де­ле­ния­ пути­ к файлу­. Путь имеет­ шесть компо­нен­тов:­ хост, устрой­ст­во,­ ката­лог,­ имя, тип и версию­. Вы може­те­ создать­ путь с помо­щью­ make-pathname с одним­ или более­ аргу­мен­та­ми­ по ключу­. В простей­шем­ случае­ доста­точ­но­ задать­ лишь имя, оста­вив­ осталь­ные­ пара­мет­ры­ со значе­ния­ми­ по умолча­нию:­

> (setf path (make-pathname :name "myfile")) #P"myfile"

Базо­вая­ функция­ для откры­тия­ файлов ­– open. Ей необ­хо­ди­мо­ сооб­щить­ путь1, а ее пове­де­ни­ем­ можно­ управлять­ с помо­щью­ много­чис­лен­ных­ ар­ гумен­тов­ по ключу­. В случае­ успе­ха­ она возвра­ща­ет­ поток,­ ука­зываю­­ щий на файл.

Вам нужно­ указать,­ как вы соби­рае­тесь­ исполь­зо­вать­ созда­вае­мый­ по­ ток. Пара­метр­ :direction опре­де­ля­ет,­ будет­ ли произ­во­дить­ся­ чтение­ (:input), запись­ (:output) или и то и другое­ одно­вре­мен­но­ (:io). Если­ поток­ исполь­зу­ет­ся­ для чтения,­ то пара­метр­ :if-exists опре­де­ля­ет­ его пове­де­­ ние в случае,­ если­ файл уже суще­ст­ву­ет­. При этом обычно­ исполь­зу­ет­ся­ :supersede (пере­за­пи­сать­ поверх)­ . Итак, созда­дим­ поток,­ через­ кото­рый­ мы сможем­ запи­сать­ что-либо­ в файл "myfile":

> (setf str (open path :direction :output :if-exists :supersede))

#<Stream C017E6>

Способ­ отобра­же­ния­ пото­ков­ при печа­ти­ зави­сит­ от исполь­зуе­мой­ реа­ лиза­ции­ Common Lisp.

Если­ теперь­ мы укажем­ поток­ str первым­ аргу­мен­том­ функции­ format, она будет­ печа­тать­ свой вывод­ в этот поток,­ а не в toplevel:

> (format str "Something~%") NIL

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

> (close str) NIL

Всегда­ закры­вай­те­ файлы­ по оконча­нии­ их исполь­зо­ва­ния­. Пока­ вы не сделае­те­ этого,­ вы не може­те­ быть увере­ны­ отно­си­тель­но­ их содер­жи­­ мого­. Если­ мы теперь­ взглянем­ на содер­жи­мое­ файла­ "myfile", то обна­­ ружим­ строчку:­

Something

1Вместо­ пути­ можно­ пере­дать­ просто­ строку,­ одна­ко­ такой­ способ­ не пере­но­­ сим между­ различ­ны­ми­ ОС.

132

Глава 7. Ввод и вывод

Если­ мы хотим­ всего­ лишь прочи­тать­ содер­жи­мое­ файла,­ то откро­ем­ поток­ с аргу­мен­том­ :direction :input:

> (setf str (open path :direction :input)) #<Stream C01C86>

Содер­жи­мое­ файла­ можно­ читать­ с помо­щью­ любой­ функции­ чтения­. Более­ подроб­но­ ввод описы­ва­ет­ся­ в разделе 7.2. При­ведем­ при­мер, в ко­ тором­ для чтения­ строки­ из файла­ исполь­зу­ет­ся­ функция­ read-line:

>(read-line str) "Something"

NIL

>(close str)

NIL

Не забудь­те­ закрыть­ файл после­ завер­ше­ния­ чтения­.

Функции­ open и close практи­че­ски­ нико­гда­ не исполь­зу­ют­ся­ явно­. Гораз­­ до удобнее­ исполь­зо­вать­ макрос­ with-open-file. Первый­ его аргу­мент ­– список,­ содер­жа­щий­ неко­то­рое­ имя пере­мен­ной­ и все те же аргу­мен­ты,­ кото­рые­ вы могли­ бы пере­дать­ функции­ open. Далее­ следу­ют­ выра­же­­ ния, кото­рые­ могут­ исполь­зо­вать­ задан­ную­ вы­ше пере­мен­ную,­ связан­­ ную внутри­ тела­ макро­са­ с именем­ пере­мен­ной­. После­ вы­полне­ния­ всех выра­же­ний­ тела­ макро­са­ поток­ закры­ва­ет­ся­ авто­ма­ти­че­ски­. Таким­ об­ разом,­ опера­ция­ запи­си­ в файл может­ быть цели­ком­ выра­же­на­ так:

(with-open-file (str path :direction :output :if-exists :supersede)

(format str "Something~%"))

Макрос­ with-open-file исполь­зу­ет­ unwind-protect (стр. 295), чтобы­ гаран­­ тировать,­ что файл будет­ дейст­ви­тель­но­ закрыт,­ даже­ если­ вы­полне­ние­ выра­же­ний­ внутри­ тела­ макро­са­ будет­ аварий­но­ прерва­но­.

7.2. Ввод

Двумя­ наибо­лее­ попу­ляр­ны­ми­ функция­ми­ чтения­ явля­ют­ся­ read-line и read. Первая­ чита­ет­ все знаки­ до нача­ла­ новой­ строки,­ возвра­щая­ их в виде­ строки­. Один ее необя­за­тель­ный­ пара­метр,­ как и для read, опре­­ деля­ет­ входной­ поток­. Если­ он не задан­ явно,­ то по умолча­нию­ исполь­­ зует­ся­ *standard-input*:

> (progn

(format t "Please enter your name: ") (read-line))

Please enter your name: Rodrigo de Bivar "Rodrigo de Bivar"

NIL

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

7.2. Ввод

133

будет­ t, если­ read-line закон­чит­ чтение­ файла­ преж­де,­ чем встретит­ ко­ нец строки­.)

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

Итак, отобра­зить­ содер­жи­мое­ файла­ в toplevel мож­но с помо­щью­ сле­ дующей­ функции:­

(defun pseudo-cat (file)

(with-open-file (str file :direction :input) (do ((line (read-line str nil ’eof)

(read-line str nil ’eof))) ((eql line ’eof))

(format t "~A~%" line))))

Если­ вы хоти­те,­ чтобы­ ввод прохо­дил­ синтак­си­че­ский­ разбор­ как объ­ екты­ Лиспа,­ исполь­зуй­те­ read. Эта функция­ считы­ва­ет­ ровно­ одно­ вы­ раже­ние­ и оста­нав­ли­ва­ет­ся­ в его конце­. Это значит,­ что она может­ счи­ тывать­ больше­ строки­ или меньше­ строки­. Разу­ме­ет­ся,­ считы­вае­мый­ объект­ должен­ быть синтак­си­че­ски­ коррект­ным­ (valid) с точки­ зрения­ синтак­си­са­ Лиспа­.

Приме­няя­ функцию­ read в toplevel, мы сможем­ исполь­зо­вать­ сколько­ угодно­ пере­но­сов­ строк внутри­ одно­го­ выра­же­ния:­

>(read)

(a b c)

(A B C)

Сдругой­ сторо­ны,­ если­ на одной­ строке­ будет­ нахо­дить­ся­ несколь­ко­ объ­ ектов­ Лиспа,­ read прекра­тит­ считы­ва­ние­ знаков,­ закон­чив­ чтение­ перво­­ го объек­та;­ остав­шие­ся­ знаки­ будут­ считы­вать­ся­ при следую­щих­ вызо­­ вах read. Прове­рим­ это предпо­ло­же­ние­ с помо­щью­ ask-number (стр. 37). Вве­дем одно­вре­мен­но­ несколь­ко­ выра­же­ний­ и посмот­рим,­ что проис­­ ходит:­

>(ask-number)

Please enter a number. a b

Please enter a number. Please enter a number. 43 43

Два при­глаше­ния­ напе­ча­та­ны­ одно­ за другим­ на одной­ строке­. Первый­ вызов­ read возвра­ща­ет­ символ­ a, не являю­щий­ся­ числом,­ поэто­му­ функ­ ция зано­во­ предла­га­ет­ ввести­ число­. Первый­ вызов­ считал­ только­ a и оста­но­вил­ся­. Поэто­му­ следую­щий­ вызов­ read сразу­ считы­ва­ет­ объект­ b, кото­рый­ так­же не число,­ и пригла­ше­ние­ выво­дит­ся­ еще раз.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]