Регистрация | Войти
Lisp — программируемый язык программирования

Файлы и директории

Проверка существования файла

Для проверки существования файла используйте функцию probe-file, которая в зависимости от наличия возвращает nil или реальное имя (truename) файла, которое не обязательно совпадает с поданным аргументом (например, в случае жесткой ссылки, hardlink).

(probe-file "/etc/passwd")
#p"/etc/passwd"
(probe-file "/etc/shadow")
#p"/etc/shadow"
(probe-file "/etc/master.passwd")
nil

Пример приведен для linux. В linux пароли пользователей хранятся в /etc/shadow, во freebsd - в /etc/master.passwd. Файл /etc/passwd есть в обеих системах.

Открытие файла

В Коммон Лиспе есть функции open и close, которые похожи на одноименные функции в других языках программирования. Тем не менее, практически всегда рекомендуется использовать вместо них макрос with-open-file. Он не только выполнит откроет файл и закроет его после выполнения операций, но и позаботится о его закрытии, если выполнение одной из этих операций приведет к аварийному завершению работы (например, при использовании throw. Типичный шаблон использования with-open-file выглядит следующим образом:

(with-open-file (str <file-spec>
                     :direction <direction>
                     :if-exists <if-exists>
                     :if-does-not-exist <if-does-not-exist>
)

  <your code here>
)

* str - переменная, которая будет связана с создаваемым при открытии файла потоком.

* <file-spec> - имя файла или путь к нему.

* <direction> может быть :input (чтение из файла), :output (запись в файл) или :io (чтение и запись одновременно. По умолчанию :input.

* <if-exists> определяет поведение в случае попытки записи в существующий файл. По умолчанию используется :error. Также вы можете использовать :supersede (замена файла), nil (переменная, ранее связанная с потоком, получит значение nil и запись в файл производиться не будет) или :rename (существующий файл переименовывается). В режиме чтения эта опция игнорируется.

* <if-does-not-exist> определяет, что делать в случае отсутствия файла. Возможные варианты: :error, :create (создается пустой файл) или nil (поток перенаправляется в nil). Поведение по умолчанию см. CLHS.

Есть и другие опции, о которых вы можете прочитать в CLHS

Чтение строки из файла

Функция read-line читает одну строку из потока (стандатрный ввод по умолчанию). Конец строки определяется по символу окончания строки. Она вернет строку без этого символа. read-line также возвращает второе значение, которое истинно, если строка была завершена без символа новой строки (фактически означает окончание файла). По умолчанию read-line вернет ошибку, если будет достигнут конец файла. Этого можно избежать, подавая nil в качестве второго аргумента. В последнем случае в случае завершения файла функция вернет nil.

(with-open-file (stream "/etc/passwd")
    (do ((line (read-line stream nil)
               (read-line stream nil)
)
)

        ((null line))
      (print line)
)
)

Если необходимо остановиться после нахождения какого-то определенного символа, этот символ подается третьим аргументом read-line:

(with-open-file (stream "/etc/passwd")
    (loop for line = (read-line stream nil 'foo)
          until (eq line 'foo)
          do (print line)
)
)

Чтение одного символа (char)

read-char очень похожа на read-line, но читает за раз один символ, а не строку. Разумеется, символ перехода на новую строку здесь не считается особым:

(with-open-file (stream "/etc/passwd")
    (do ((char (read-char stream nil)
               (read-char stream nil)
)
)

        ((null char))
      (print char)
)
)

Чтение символа без его удаления

Чтобы прочитать из потока символ без его удаления из этого потока, воспользуйтесь функцией peek-char. Вообще говоря, она может быть использована с тремя различными целями в зависимости от значения первого аргумента. Если первый аргумент nil, то она вернет следующий символ, ожидающий своей очереди в потоке:

(with-input-from-string (stream "I'm not amused")
    (print (read-char stream))
    (print (peek-char nil stream))
    (print (read-char stream))
    (values)
)


#\I
#\'
#\'

Если первый аргумент t, то peak-char будет пропускать пустые (whitespace) символы так, как будто все пробелы, переносы и табуляторы были прочитаны функцией read-char:

(with-input-from-string (stream "I'm 
                                   not amused"
)

    (print (read-char stream))
    (print (read-char stream))
    (print (read-char stream))
    (print (peek-char t stream))
    (print (read-char stream))
    (print (read-char stream))
    (values)
)

#\I
#\'
#\m
#\n
#\n
#\o

Если первый аргумент - символ, то peak-char пропускает все символы, пока не встретит этот символ.

(with-input-from-string (stream "I'm not amused")
    (print (read-char stream))
    (print (peek-char #\a stream))
    (print (read-char stream))
    (print (read-char stream))
    (values)
)

#\I
#\a
#\a
#\m

read-char имеет другие опционные аргументы, которые определяют поведение в случае достижения конца файла, так же, как и аналогичные в read-line и read-char:

(with-input-from-string (stream "I'm not amused")
    (print (read-char stream))
    (print (peek-char #\d stream))
    (print (read-char stream))
    (print (peek-char nil stream nil 'the-end))
    (values)
)

#\I
#\d
#\d
THE-END

Вы также можете положить один символ назад в поток с помощью функции unread-char. Используйте его так, как если бы вы захотели использовать reak-char вместо read-char уже после прочтения символа:

(with-input-from-string (stream "I'm not amused")
    (let ((c (read-char stream)))
      (print c)
      (unread-char c stream)
      (print (read-char stream))
      (values)
)
)

#\I
#\I

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

Свободный доступ к файлу (random access)

Для свободного доступа к любому месту файла используйте file position. Если функция получает только один аргумент (сам поток), то она возвращает текущее положение в файле. Изменить текущее положение можно с помощью второго аргумента, который должен быть целым числом:

(with-input-from-string (stream "I'm not amused")
    (print (file-position stream))
    (print (read-char stream))
    (print (file-position stream))
    (file-position stream 4)
    (print (file-position stream))
    (print (read-char stream))
    (print (file-position stream))
    (values)
)

0
#\I
1
4
#\n
5
@2009-2013 lisper.ru