module IO ( Handle, HandlePosn, IOMode(ReadMode,WriteMode,AppendMode,ReadWriteMode), BufferMode(NoBuffering,LineBuffering,BlockBuffering), SeekMode(AbsoluteSeek,RelativeSeek,SeekFromEnd), stdin, stdout, stderr, openFile, hClose, hFileSize, hIsEOF, isEOF, hSetBuffering, hGetBuffering, hFlush, hGetPosn, hSetPosn, hSeek, hWaitForInput, hReady, hGetChar, hGetLine, hLookAhead, hGetContents, hPutChar, hPutStr, hPutStrLn, hPrint, hIsOpen, hIsClosed, hIsReadable, hIsWritable, hIsSeekable, isAlreadyExistsError, isDoesNotExistError, isAlreadyInUseError, isFullError, isEOFError, isIllegalOperation, isPermissionError, isUserError, ioeGetErrorString, ioeGetHandle, ioeGetFileName, try, bracket, bracket_, -- ...и то, что экспортирует Prelude IO, FilePath, IOError, ioError, userError, catch, interact, putChar, putStr, putStrLn, print, getChar, getLine, getContents, readFile, writeFile, appendFile, readIO, readLn ) where import Ix(Ix) data Handle = ... -- зависит от реализации instance Eq Handle where ... instance Show Handle where .. -- зависит от реализации data HandlePosn = ... -- зависит от реализации instance Eq HandlePosn where ... instance Show HandlePosn where --- -- зависит от реализации data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode deriving (Eq, Ord, Ix, Bounded, Enum, Read, Show) data BufferMode = NoBuffering | LineBuffering | BlockBuffering (Maybe Int) deriving (Eq, Ord, Read, Show) data SeekMode = AbsoluteSeek | RelativeSeek | SeekFromEnd deriving (Eq, Ord, Ix, Bounded, Enum, Read, Show) stdin, stdout, stderr :: Handle openFile :: FilePath -> IOMode -> IO Handle hClose :: Handle -> IO () |
hFileSize :: Handle -> IO Integer hIsEOF :: Handle -> IO Bool isEOF :: IO Bool isEOF = hIsEOF stdin hSetBuffering :: Handle -> BufferMode -> IO () hGetBuffering :: Handle -> IO BufferMode hFlush :: Handle -> IO () hGetPosn :: Handle -> IO HandlePosn hSetPosn :: HandlePosn -> IO () hSeek :: Handle -> SeekMode -> Integer -> IO () hWaitForInput :: Handle -> Int -> IO Bool hReady :: Handle -> IO Bool hReady h = hWaitForInput h 0 hGetChar :: Handle -> IO Char hGetLine :: Handle -> IO String hLookAhead :: Handle -> IO Char hGetContents :: Handle -> IO String hPutChar :: Handle -> Char -> IO () hPutStr :: Handle -> String -> IO () hPutStrLn :: Handle -> String -> IO () hPrint :: Show a => Handle -> a -> IO () hIsOpen :: Handle -> IO Bool hIsClosed :: Handle -> IO Bool hIsReadable :: Handle -> IO Bool hIsWritable :: Handle -> IO Bool hIsSeekable :: Handle -> IO Bool isAlreadyExistsError :: IOError -> Bool isDoesNotExistError :: IOError -> Bool isAlreadyInUseError :: IOError -> Bool isFullError :: IOError -> Bool isEOFError :: IOError -> Bool isIllegalOperation :: IOError -> Bool isPermissionError :: IOError -> Bool isUserError :: IOError -> Bool ioeGetErrorString :: IOError -> String ioeGetHandle :: IOError -> Maybe Handle ioeGetFileName :: IOError -> Maybe FilePath try :: IO a -> IO (Either IOError a) bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c bracket_ :: IO a -> (a -> IO b) -> IO c -> IO c |
Монадическая система ввода - вывода, используемая в Haskell , описана в описании языка Haskell . Общеупотребительные функции ввода - вывода, такие как print, являются частью стандартного начала (Prelude) и нет необходимости их явно импортировать. Эта библиотека содержит более продвинутые средства ввода - вывода. Некоторые относящиеся к ним операции над файловыми системами содержатся в библиотеке Directory.
Любая функция, которая возвращает результат IO, может завершиться с ошибкой isIllegalOperation. Дополнительные ошибки, которые могут быть вызваны реализацией, перечислены после соответствующей операции. В некоторых случаях реализация не способна различить возможные причины ошибки. В этом случае она должна вернуть isIllegalOperation.
Имеются три дополнительные функции для того, чтобы получить информацию о значении ошибки, --- это ioeGetHandle, которая возвращает Just hdl, если значение ошибки относится к дескриптору hdl, и Nothing иначе; ioeGetFileName, которая возвращает Just имя, если значение ошибки относится к файлу имя, и Nothing иначе; и ioeGetErrorString, которая возвращает строку. Для "пользовательских" ошибок (которые вызваны использованием fail), строка, возвращенная ioeGetErrorString, является аргументом, который был передан в fail; для всех остальных ошибок строка зависит от реализации.
Функция try возвращает ошибку в вычислении, явно использующем тип Either .
Функция bracket охватывает обычный способ выделения памяти, вычисления и освобождения памяти, в котором шаг освобождения должен произойти даже в случае ошибки во время вычисления. Это аналогично try-catch-finally в Java.
Имена файлов и каталогов являются значениями типа String, их точный смысл зависит от операционной системы. Файлы могут быть открыты; результатом открытия файла является дескриптор, который можно затем использовать для работы с содержимом этого файла.
Haskell определяет операции для чтения и записи символов соответственно из файла и в файл, они представлены значениями типа Handle. Каждое значение этого типа является дескриптором: записью, используемой системой поддержки выполнения программ Haskell для управления вводом - выводом объектов файловой системы. Дескриптор имеет по крайней мере следующие признаки:
Большинство дескрипторов будет также иметь текущую позицию ввода - вывода, указывающую где произойдет следующая операция ввода или вывода. Дескриптор является читаемым, если он управляет только вводом или и вводом, и выводом; аналогично, он является записываемым, если он управляет только выводом или и вводом, и выводом. Дескриптор является открытым, когда он впервые назначается. Как только он закрывается, его больше нельзя использовать ни для ввода, ни для вывода, хотя реализация не может повторно использовать его память, пока остаются ссылки на него. Дескрипторы находятся в классах Show и Eq. Строка, полученная в результате вывода дескриптора, зависит от системы; она должна включать достаточно информации, чтобы идентифицировать дескриптор для отладки. В соответствии с == дескриптор равен только самому себе; не делается никаких попыток сравнить внутреннее состояние различных дескрипторов на равенство.
Во время инициализации программы назначаются три дескриптора. Первые два (stdin и stdout) управляют вводом или выводом из стандартных каналов ввода или вывода программы на Haskell соответственно. Третий (stderr) управляет выводом в стандартный канал вывода ошибок. Эти дескрипторы первоначально открыты.
Операция hGetContents hdl (раздел 21.9.4) помещает дескриптор hdl в промежуточное состояние --- полузакрытое. В этом состоянии hdl фактически закрыт, но элементы читаются из hdl по требованию и накапливаются в специальном списке, возвращаемом hGetContents hdl.
Любая операция, которая завершается неуспешно из-за того, что дескриптор закрыт, также завершится неуспешно, если дескриптор полузакрыт. Единственное исключение --- hClose. Полузакрытый дескриптор становится закрытым, если:
Как только полузакрытый дескриптор становится закрытым, содержимое связанного с ним списка становится постоянным. Содержимое этого окончательного списка определено только частично: список будет содержать по крайней мере все элементы потока, которые были вычислены до того, как дескриптор стал закрытым.
Любые ошибки ввода - вывода, которые возникли в то время, когда дескриптор был полузакрыт, просто игнорируются.
Реализации должны по возможности вызывать по крайней мере локально по отношению к процессу Haskell блокировку файлов со множественным чтением и единственной записью. То есть может быть или много дескрипторов одного и того же файла, которые управляют вводом, или только один дескриптор файла, который управляет выводом. Если какой-либо открытый или полузакрытый дескриптор управляет файлом для вывода, никакой новый дескриптор не может быть назначен для этого файла. Если какой-либо открытый или полузакрытый дескриптор управляет файлом для ввода, новые дескрипторы могут быть назначены, только если они не управляют выводом. Хотя совпадение двух файлов зависит от реализации, но они обычно должны быть одинаковыми, если они имеют одинаковый абсолютный путь и ни один из них не был переименован, например.
Предупреждение: операция readFile (раздел 7.1) хранит полузакрытый дескриптор файла до тех пор, пока все содержимое файла не будет считано. Из этого следует, что попытка записать в файл (используя writeFile, например), который было ранее открыт с помощью readFile, обычно завершается с ошибкой isAlreadyInUseError.
Функция openFile файл режим назначает и возвращает новый, открытый дескриптор для управления файлом файл. Он управляет вводом, если режим равен ReadMode, выводом, если режим равен WriteMode или AppendMode, и вводом и выводом, если режим равен ReadWriteMode.
Если файл не существует и открывается для вывода, должен быть создан новый файл. Если режим равен WriteMode и файл уже существует, то файл должен быть усечен до нулевой длины. Некоторые операционные системы удаляют пустые файлы, поэтому нет гарантии, что файл будет существовать после openFile с режимом WriteMode, если он не будет впоследствии успешно записан. Дескриптор устанавливается в конец файла, если режим равен AppendMode, иначе --- в начало файла (в этом случае его внутренняя позиция ввода - вывода равна 0). Начальный режим буферизации зависит от реализации.
Если openFile завершается неуспешно для файла, открываемого для вывода, файл тем не менее может быть создан, если он уже не существует.
Сообщения об ошибках: функция openFile может завершиться с ошибкой isAlreadyInUseError, если файл уже открыт и не может быть повторно открыт; isDoesNotExistError, если файл не существует, или isPermissionError, если пользователь не имеет прав на открытие файла.
Функция hClose hdl делает дескриптор hdl закрытым. До того как завершится вычисление функции, если hdl является записываемым, то его буфер сбрасывается на диск как при использовании hFlush. Выполнение hClose на дескрипторе, который уже был закрыт, не влечет никаких действий; такое выполнение не является ошибкой. Все остальные операции на закрытом дескрипторе будут завершаться с ошибкой. Если hClose завершается неуспешно по какой-либо причине, все дальнейшие операции (кроме hClose) на дескрипторе будут тем не менее завершаться неуспешно, как будто hdl был успешно закрыт.
Для дескриптора hdl, который прикреплен к физическому файлу, hFileSize hdl возвращает размер этого файла в 8-битных байтах (>= 0).
Для читаемого дескриптора hdl функция hIsEOF hdl возвращает True, если никакой дальнейший ввод не может быть получен из hdl; для дескриптора, прикрепленного к физическому файлу, это означает, что текущая позиция ввода - вывода равна длине файла. В противном случае функция возвращает False. Функция isEOF идентична описанной функции, за исключением того, что она работает только с stdin.
Поддерживаются три вида буферизации: буферизация строк, буферизация блоков или отсутствие буферизации. Эти режимы имеют следующие результаты. При выводе элементы записываются или сбрасываются на диск из внутреннего буфера в соответствии с режимом буферизации:
Аналогично, в соответствии с режимом буферизации для дескриптора hdl выполняется ввод:
В большинстве реализаций для физических файлов обычно применяется буферизация блоков, а для терминалов обычно применяется буферизация строк.
Функция hSetBuffering hdl режим устанавливает режим буферизации для дескриптора hdl для последующих чтений и записей.
Если режим буферизации BlockBuffering или LineBuffering изменен на NoBuffering, тогда
Сообщения об ошибках: функция hSetBuffering может завершиться с ошибкой isPermissionError, если дескриптор уже использовался для чтения или записи и реализация не позволяет изменить режим буферизации.
Функция hGetBuffering hdl возвращает текущий режим буферизации для hdl.
Режим буферизации по умолчанию при открытии дескриптора зависит от реализации и может зависеть от объекта файловой системы, к которому прикреплен дескриптор.
Функция hFlush hdl заставляет любые элементы, буферизированные для вывода в дескрипторе hdl, тотчас же передать операционной системе.
Сообщения об ошибках: функция hFlush может завершиться с ошибкой isFullError, если устройство заполнено; isPermissionError, если превышены пределы системных ресурсов. При этих обстоятельствах не определено, будут ли символы в буфере проигнорированы или останутся в буфере.
Функция hGetPosn hdl возвращает текущую позицию ввода - вывода hdl в виде значения абстрактного типа HandlePosn. Если вызов hGetPosn h возвращает позицию p, тогда функция hSetPosn p устанавливает позицию h в позицию, которую она содержала во время вызова hGetPosn.
Сообщения об ошибках: функция hSetPosn может завершиться с ошибкой isPermissionError, если были превышены пределы ресурсов системы.
Функция hSeek hdl режим i устанавливает позицию дескриптора hdl в зависимости от режима. Если режим равен:
Если для hdl используется режим буферизации строк или блоков, то установка позиции, которая не находится в текущем буфере, приведет к тому, что сначала будут записаны в устройство все элементы в выходном буфере, а затем входной буфер будет проигнорирован. Некоторые дескрипторы не могут использоваться для установки позиции (см. hIsSeekable) или могут поддерживать только подмножество возможных операций позиционирования (например, только возможность установить дескриптор в конец ленты или возможность сместить дескриптор в положительном направлении от начала файла или от текущей позиции). Невозможно установить отрицательную позицию ввода - вывода или для физического файла установить позицию ввода - вывода за пределы текущего конца файла.
Сообщения об ошибках: функция hSeek может завершиться с ошибкой isPermissionError, если были превышены пределы системных ресурсов.
Функции hIsOpen, hIsClosed, hIsReadable, hIsWritable и hIsSeekable возвращают информацию о свойствах дескриптора. Каждый из возвращаемых результатов равен True, если дескриптор обладает указанным свойством, и False иначе.
Здесь мы дадим определения стандартного набора операций ввода для чтения символов и строк из текстовых файлов, использующих дескрипторы. Многие из этих функций являются обобщениями функций Prelude. Ввод - вывод в Prelude как правило использует stdin и stdout; здесь дескрипторы явно определены с помощью операцией ввода - вывода.
Функция hWaitForInput hdl t ждет, пока не станет доступным ввод по дескриптору hdl. Она возвращает True, как только станет доступным ввод для hdl, или False, если ввод не доступен в пределах t миллисекунд.
Функция hReady hdl указывает, есть ли по крайней мере один элемент, доступный для ввода из дескриптора hdl.
Сообщения об ошибках: функции hWaitForInput и hReady завершаются с ошибкой isEOFError, если был достигнут конец файла.
Функция hGetChar hdl считывает символ из файла или канала, управляемого hdl.
Функция hGetLine hdl считывает строку из файла или канала, управляемого hdl. getLine из Prelude является краткой записью для hGetLine stdin.
Сообщения об ошибках: функция hGetChar завершается с ошибкой isEOFError, если был достигнут конец файла. Функция hGetLine завершается с ошибкой isEOFError, если конец файла был обнаружен при считывании первого символа строки. Если hGetLine обнаружит конец файла в любом другом месте при считывании строки, он будет обработан как признак конца строки и будет возвращена (неполная) строка.
Функция hLookAhead hdl возвращает следующий символ из дескриптора hdl, не удаляя его из входного буфера; она блокируется до тех пор, пока символ не станет доступен.
Сообщения об ошибках: функция hLookAhead может завершиться с ошибкой isEOFError, если был достигнут конец файла.
Функция hGetContents hdl возвращает список символов, соответствующих непрочитанной части канала или файла, управляемого hdl, который сделан полузакрытым.
Сообщения об ошибках: функция hGetContents может завершиться с ошибкой isEOFError, если был достигнут конец файла.
Функция hPutChar hdl c записывает символ c в файл или канал, управляемый hdl. Символы могут быть буферизированы, если включена буферизация для hdl.
Функция hPutStr hdl s записывает строку s в файл или канал, управляемый hdl.
Функция hPrint hdl t записывает строковое представление t, полученное функцией shows, в файл или канал, управляемый hdl, и добавляет в конец символ новой строки.
Сообщения об ошибках: функции hPutChar, hPutStr и hPrint могут завершиться с ошибкой isFullError, если устройство заполнено, или isPermissionError, если превышены пределы других системных ресурсов.
Рассмотрим некоторые простые примеры, иллюстрирующие ввод - вывод в Haskell .
Эта программа считывает и суммирует два числа типа Integer.
import IO
main = do
hSetBuffering stdout NoBuffering
putStr "Введите целое число: "
x1 <- readNum
putStr "Введите другое целое число: "
x2 <- readNum
putStr ("Их сумма равна " ++ show (x1+x2) ++ "\n")
where readNum :: IO Integer
-- Указание сигнатуры типа позволяет избежать
-- исправления типов x1,x2 правилом по умолчанию
readNum = readLn
Простая программа для создания копии файла, в которой все символы в нижнем регистре
заменены на символы в верхнем регистре. Эта программа не разрешает копировать
файл в самого себя. Эта версия использует ввод - вывод символьного уровня.
Обратите внимание, что ровно два аргумента должны быть переданы программе.
import IO
import System
import Char( toUpper )
main = do
[f1,f2] <- getArgs
h1 <- openFile f1 ReadMode
h2 <- openFile f2 WriteMode
copyFile h1 h2
hClose h1
hClose h2
copyFile h1 h2 = do
eof <- hIsEOF h1
if eof then return () else
do
c <- hGetChar h1
hPutChar h2 (toUpper c)
copyFile h1 h2
Эквивалентная, но значительно более короткая версия, использующая ввод - вывод строк:
import System
import Char( toUpper )
main = do
[f1,f2] <- getArgs
s <- readFile f1
writeFile f2 (map toUpper s)
module IO {- список экспорта пропущен -} where
-- Только обеспечивает реализацию не зависящих от системы
-- действий, которые экспортирует IO.
try :: IO a -> IO (Either IOError a)
try f = catch (do r <- f
return (Right r))
(return . Left)
bracket :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket before after m = do
x <- before
rs <- try (m x)
after x
case rs of
Right r -> return r
Left e -> ioError e
-- вариант приведенного выше, где средняя функция не требует x
bracket_ :: IO a -> (a -> IO b) -> IO c -> IO c
bracket_ before after m = do
x <- before
rs <- try m
after x
case rs of
Right r -> return r
Left e -> ioError e