Модуль Scanf
module Scanf: sig .. end
Функции форматированного ввода.
Введение
Функциональный ввод с использованием строк формата
Модуль Scanf предоставляет функции форматированного ввода или сканеры.
Функции форматированного ввода могут считывать данные из любого типа источника, включая строки, файлы или любые объекты, возвращающие символы. Более общий источник символов называется каналом форматированного ввода (или буфером сканирования) и имеет тип Scanf.Scanning.in_channel. Более общая функция форматированного ввода считывает данные из любого буфера сканирования и называется bscanf.
В общем случае, функции форматированного ввода имеют 3 аргумента:
- первый аргумент — источник символов для ввода,
- второй аргумент — строка формата, которая определяет значения для считывания,
- третий аргумент — функция-приемник, которая применяется к считанным значениям.
Следовательно, типичный вызов функции форматированного ввода Scanf.bscanf выглядит как bscanf ic fmt f, где:
-
ic— источник символов (обычно канал форматированного ввода с типомScanf.Scanning.in_channel),
-
fmt— строка формата (такие же строки формата, как и те, что используются для вывода с модулемPrintfилиFormat),
-
f— функция, которая имеет столько аргументов, сколько значений нужно считать из ввода в соответствии сfmt.
Простой пример
Как указано выше, выражение bscanf ic "%d" f считывает десятичное целое число n из источника символов ic и возвращает f n.
Например,
- если мы используем
stdinв качестве источника символов (Scanf.Scanning.stdin— предопределенный канал форматированного ввода, который считывает данные со стандартного ввода),
- если мы определим функцию-приемник
fкакlet f x = x + 1,
то bscanf Scanning.stdin "%d" f считывает целое число n со стандартного ввода и возвращает f n (то есть n + 1). Таким образом, если мы выполним bscanf stdin "%d" f, а затем введём 41 с клавиатуры, результат будет 42.
Форматированный ввод как функциональная особенность
Возможности сканирования в OCaml напоминают аналогичные возможности C. Однако они также существенно отличаются, проще и при этом мощнее: функции форматированного ввода являются высшими функциональными объектами, а механизм передачи параметров — это просто обычное применение функций, а не механизм присваивания переменных, типичный для форматированного ввода в императивных языках; строки формата OCaml также содержат полезные дополнения для лёгкого определения сложных токенов; как ожидается в функциональном языке программирования, функции форматированного ввода также поддерживают полиморфизм, в частности, произвольное взаимодействие с полиморфными пользовательскими сканерами. Кроме того, механизм форматированного ввода OCaml полностью проверяется на этапе компиляции.
Канал форматированного ввода
module Scanning: sig .. end
Тип функций форматированного ввода
type ('a, 'b, 'c, 'd) scanner = ('a, Scanning.in_channel, 'b, 'c, 'a -> 'd, 'd) format6 -> 'c Тип сканеров форматированного ввода: ('a, 'b, 'c, 'd) scanner — это тип функции форматированного ввода, которая считывает данные из канала форматированного ввода в соответствии со строкой формата; точнее, если scan — это некоторая функция форматированного ввода, то scan применяет
ic fmt ff ко всем аргументам, указанным в строке формата fmt, когда scan прочитал эти аргументы из канала форматированного ввода Scanf.Scanning.in_channel ic.
Например, функция Scanf.scanf ниже имеет тип ('a, 'b, 'c, 'd) scanner, так как это функция форматированного ввода, которая считывает данные из Scanf.Scanning.stdin: scanf fmt f применяет f к аргументам, указанным в fmt, считывая эти аргументы из stdin, как и ожидалось.
Если формат fmt имеет какие-либо %r указания, соответствующие функции форматированного ввода должны быть предоставлены до функции-приемника f. Например, если read_elem — это функция ввода для значений типа t, то bscanf ic "%r;" read_elem f считывает значение v типа t за которым следует символ ';' и возвращает f v.
- Since 3.10.0
exception Scan_failure of string
Если входные данные не могут быть прочитаны в соответствии со спецификацией строки формата, функции форматированного ввода обычно генерируют исключение Scan_failure.
Общая функция форматированного ввода
val bscanf : Scanning.in_channel -> ('a, 'b, 'c, 'd) scanner
bscanf ic fmt r1 ... rN f считывает символы из канала форматированного ввода Scanf.Scanning.in_channel ic и преобразует их в значения в соответствии со строкой формата fmt. На заключительном этапе функция-приемник f применяется к прочитанным значениям и возвращает результат вызова bscanf.
Например, если f — это функция fun s i -> i + 1, то Scanf.sscanf "x= 1" "%s = %i" f возвращает 2.
Аргументы r1 по rN — это пользовательские функции ввода, которые считывают аргументы, соответствующие %r преобразованиям, указанным в строке формата.
Описание строки формата
Строка формата — это строка символов, которая содержит три типа объектов:
- обычные символы, которые просто сопоставляются с символами входных данных (с особым случаем для пробелов и символов новой строки, см. Символ пробела в строках формата),
- спецификации преобразования, каждая из которых вызывает считывание и преобразование одного аргумента для функции
f(см. Спецификации преобразования в строках формата), - указатели сканирования для определения границ токенов (см. сканирование Указатели сканирования в строках формата).
Символ пробела в строках формата
Как упоминалось выше, обычный символ в строке формата просто сопоставляется с последующим символом входных данных; однако, есть два исключения из этого правила: символ пробела (' ' или ASCII-код 32) и символ новой строки ('\n' или ASCII-код 10). Пробел не соответствует одному символу пробела, а любому количеству «пробелов» во входных данных. Точнее, пробел внутри строки формата соответствует любому количеству символов табуляции, пробела, новой строки и возврата каретки. Аналогично, символ новой строки в строке формата соответствует либо одиночной новой строке, либо возврату каретки, за которым следует новая строка.
Сопоставляя любое количество пробелов, пробел в строке формата также соответствует отсутствию пробелов; следовательно, вызов bscanf ib успешно выполняется и возвращает
"Price = %d $" (fun p -> p)1 при чтении входных данных с различными пробелами, такими как Price = 1 $, Price = 1 $ или даже Price=1$.
Спецификации преобразования в строках формата
Спецификации преобразования состоят из символа %, за которым следует необязательный флаг, необязательная ширина поля и один или два символа преобразования.
Символы преобразования и их значения:
-
d: считывает необязательно со знаком десятичное целое число (0-9+). -
i: считывает необязательно со знаком целое число (обычные соглашения о вводе для десятичных (0-9+), шестнадцатеричных (0x[0-9a-f]+и0X[0-9A-F]+), восьмеричных (0o[0-7]+) и двоичных (0b[0-1]+) обозначений поняты). -
u: считывает целое десятичное число без знака. -
xилиX: считывает целое шестнадцатеричное число без знака ([0-9a-fA-F]+). -
o: считывает целое восьмеричное число без знака ([0-7]+). -
s: считывает строковый аргумент, который распространяется по возможности до тех пор, пока не выполнится следующее граничное условие:- найден пробел (см. Пробельный символ в строках формата),
- встречено указание сканирования (см. сканирование Указания сканирования в строках формата),
- достигнут конец ввода.
-
S: считывает аргумент строки с разделителями (разделители и специальные экранированные символы следуют лексическим соглашениям OCaml). -
c: считывает одиночный символ. Чтобы проверить текущий символ ввода без его чтения, укажите нулевую ширину поля, т. е. используйте спецификацию%0c. ПоднятьInvalid_argument, если спецификация ширины поля больше 1. -
C: считывает одиночный символ с разделителем (разделители и специальные экранированные символы следуют лексическим соглашениям OCaml). -
f,e,E,g,G: считывает необязательно со знаком число с плавающей точкой в десятичной записи, в стилеdddd.ddd.
e/E+-dd -
h,H: считывает необязательно со знаком число с плавающей точкой в шестнадцатеричной записи. -
F: считывает число с плавающей точкой в соответствии с лексическими соглашениями OCaml (следовательно, десятичная точка является обязательной, если часть экспоненты не указана). -
B: считывает аргумент булевого типа (trueилиfalse). -
b: считывает аргумент булевого типа (для обратной совместимости; не использовать в новых программах). -
ld,li,lu,lx,lX,lo: считывает аргументint32в формате, заданном второй буквой для обычных целых чисел. -
nd,ni,nu,nx,nX,no: считывает аргументnativeintв формате, заданном второй буквой для обычных целых чисел. -
Ld,Li,Lu,Lx,LX,Lo: считывает аргументint64в формате, заданном второй буквой для обычных целых чисел. -
[ range ]: считывает символы, соответствующие одному из символов, указанных в диапазоне символовrange(или не указанных в нем, если диапазон начинается с^). Считываетstring, которое может быть пустым, если следующий символ ввода не соответствует диапазону. Множество символов отc1доc2(включительно) обозначается какc1-c2. Следовательно,%[0-9]возвращает строку, представляющую десятичное число, или пустую строку, если десятичная цифра не найдена; аналогично,%[0-9a-f]возвращает строку шестнадцатеричных цифр. Если закрывающая скобка появляется в диапазоне, она должна появляться в качестве первого символа диапазона (или сразу после^в случае отрицания диапазона); следовательно[]]соответствует символу], а[^]]соответствует любому символу, который не является]. Используйте%%и%@для включения%или@в диапазон. -
r: пользовательский читатель. Принимает следующую функцию форматированного вводаriи применяет её к буферу сканированияibдля чтения следующего аргумента. Функция вводаriдолжна иметь типScanning.in_channel -> 'a, а считанный аргумент – тип'a. -
{ fmt %}: считывает аргумент строки формата. Считанная строка формата должна иметь тот же тип, что и спецификация строки форматаfmt. Например,"%{ %i %}"считывает любую строку формата, которая может считывать значение типаint; следовательно, еслиsявляется строкой"fmt:\"number is %u\"", тоScanf.sscanf s "fmt: %{%i%}"выполняется успешно и возвращает строку формата"number is %u". -
( fmt %): замена подформата сканирования. Считывает строку форматаrfв вводе, а затем продолжает сканирование сrfвместо сканирования сfmt. Строка форматаrfдолжна иметь тот же тип, что и спецификация строки форматаfmt, которую она заменяет. Например,"%( %i %)"считывает любую строку формата, которая может считывать значение типаint. Преобразование возвращает считанную строку форматаrf, а затем значение, считанное с помощьюrf. Следовательно, еслиsявляется строкой"\"%4d\"1234.00", тоScanf.sscanf s "%(%i%)" (fun fmt i -> fmt, i)вычисляется как("%4d", 1234). Это поведение не является просто заменой формата, так как преобразование возвращает считанную строку формата в качестве дополнительного аргумента. Если вам нужна чисто замена формата, используйте специальный флаг_для отбрасывания лишнего аргумента: преобразование%_( fmt %)считывает строку форматаrfи затем ведет себя так же, как строка форматаrf. Таким образом, еслиsявляется строкой"\"%4d\"1234.00", тоScanf.sscanf s "%_(%i%)"просто эквивалентноScanf.sscanf "1234.00" "%4d". -
l: возвращает количество прочитанных строк до сих пор. -
n: возвращает количество прочитанных символов до сих пор. -
NилиL: возвращает количество прочитанных токенов до сих пор. -
!: соответствует условию конца ввода. -
%: соответствует одному символу%во входных данных. -
@: соответствует одному символу@во входных данных. -
,: ничего не делает.
После символа %, вводящего преобразование, может появиться специальный флаг _: последующее преобразование происходит как обычно, но результирующее значение отбрасывается. Например, если f – это функция fun i -> i + 1, а s – это строка "x = 1", то Scanf.sscanf s "%_s = %i" f возвращает 2.
Ширина поля состоит из необязательного целочисленного литерала, указывающего максимальную ширину токена для чтения. Например, %6d считывает целое число, имеющее не более 6 десятичных цифр; %4f считывает число с плавающей точкой с максимальным количеством 4 символов; а %8[\000-\255] возвращает следующие 8 символов (или все доступные символы, если доступно меньше 8 символов во вводе).
Примечания:
- как упоминалось выше, преобразование
%sвсегда выполняется успешно, даже если в вводе ничего нет для чтения: в этом случае оно просто возвращает"".
- кроме соответствующих цифр, символы
'_'могут появляться внутри чисел (это напоминает обычные лексические соглашения OCaml). Если требуется более строгое сканирование, используйте средство преобразования диапазона вместо преобразований чисел.
- возможность
scanfне предназначена для сложного лексического анализа и разбора. Если она кажется недостаточно выразительной для ваших потребностей, существуют несколько альтернатив: регулярные выражения (модульStr), парсеры потоков,ocamllex-генераторы лексеров,ocamlyacc-генераторы парсеров.
Указания сканирования в строках формата
Указания сканирования появляются сразу после преобразований строк %s и %[ range ] для обозначения конца токена. Указание сканирования вводится символом @, за которым следует некоторый простой символ c. Это означает, что токен строки должен заканчиваться непосредственно перед следующим соответствующим символом c (который пропускается). Если символ c не встретится, токен строки распространяется по возможности. Например, "%s@\t" считывает строку до следующего символа табуляции или до конца ввода. Если символ @ появляется где-либо еще в строке формата, он обрабатывается как обычный символ.
Примечание:
- Как обычно в строках формата, символы
%и@должны быть экранированы с помощью%%и%@; это правило сохраняется и внутри спецификаций диапазона и указаний сканирования. Например, формат"%s@%%"считывает строку до следующего символа%, а формат"%s@%@"считывает строку до следующего символа@. - Указания сканирования вносят незначительные отличия в синтаксис строк формата
Scanfпо сравнению со строками формата модуляPrintf. Однако, указания сканирования аналогичны тем, которые используются в модулеFormat; следовательно, при создании форматированного текста для сканирования модулемScanf.bscanf, разумно использовать функции печати из модуляFormat(или, если вам нужно использовать функции из модуляPrintf, избегайте или тщательно проверяйте строки формата, содержащие символы'@').
Исключения во время сканирования
Сканеры могут генерировать следующие исключения, когда входные данные не могут быть прочитаны в соответствии со строкой формата:
- Поднять
Scanf.Scan_failure, если входные данные не соответствуют формату.
- Поднять
Failureесли преобразование в число невозможно.
- Вызовите
End_of_file, если при чтении текущего спецификации преобразования требуется больше символов, чем доступно ввода.
- Вызовите
Invalid_argument, если строка формата неверна.
Примечание:
- вследствие этого, сканирование
%sпреобразования никогда не вызывает исключениеEnd_of_file: если достигнут конец ввода, преобразование завершается успешно и просто возвращает прочитанные до этого символы, или""если не было прочитано ни одного.
Специализированные функции форматированного ввода
val sscanf : string -> ('a, 'b, 'c, 'd) scanner
Аналогично Scanf.bscanf, но читает из заданной строки.
val scanf : ('a, 'b, 'c, 'd) scanner
Аналогично Scanf.bscanf, но читает из предопределённого форматированного канала ввода Scanf.Scanning.stdin, подключенного к stdin.
val kscanf : Scanning.in_channel -> (Scanning.in_channel -> exn -> 'd) -> ('a, 'b, 'c, 'd) scanner
Аналогично Scanf.bscanf, но принимает дополнительный аргумент функции ef, который вызывается в случае ошибки: если процесс сканирования или какое-либо преобразование завершается неудачно, функция сканирования прерывается и вызывает функцию обработки ошибок ef с форматированным каналом ввода и исключением, прервавшим процесс сканирования, в качестве аргументов.
val ksscanf : string -> (Scanning.in_channel -> exn -> 'd) -> ('a, 'b, 'c, 'd) scanner
Аналогично Scanf.kscanf, но читает из заданной строки.
- Since 4.02.0
Чтение строк формата из ввода
val bscanf_format : Scanning.in_channel -> ('a, 'b, 'c, 'd, 'e, 'f) format6 -> (('a, 'b, 'c, 'd, 'e, 'f) format6 -> 'g) -> 'g
bscanf_format ic fmt f считывает маркер строки формата из форматированного канала ввода ic, согласно заданной строке формата fmt, и применяет f к значению полученной строки формата.
- Since 3.09.0
-
Вызывает
Scan_failureесли значение прочитанной строки формата не имеет того же типа, что иfmt.
val sscanf_format : string -> ('a, 'b, 'c, 'd, 'e, 'f) format6 -> (('a, 'b, 'c, 'd, 'e, 'f) format6 -> 'g) -> 'g
Аналогично Scanf.bscanf_format, но читает из заданной строки.
- Since 3.09.0
val format_from_string : string -> ('a, 'b, 'c, 'd, 'e, 'f) format6 -> ('a, 'b, 'c, 'd, 'e, 'f) format6
format_from_string s fmt преобразует строковый аргумент в строку формата согласно заданной строке формата fmt.
- Since 3.10.0
-
Вызывает
Scan_failureеслиs, рассматриваемая как строка формата, не имеет того же типа, что иfmt.
val unescaped : string -> string
unescaped s возвращает копию s с последовательностями escape (согласно лексическим соглашениям OCaml) заменёнными соответствующими специальными символами. Более точно, Scanf.unescaped обладает следующим свойством: для всех строк s, Scanf.unescaped (String.escaped s) = s.
Всегда возвращает копию аргумента, даже если в аргументе нет последовательностей escape.
- Since 4.00.0
-
Вызывает
Scan_failureеслиsне правильно экранирована (т.е.sсодержит неверные escape-последовательности или специальные символы, которые не правильно экранированы). Например,Scanf.unescaped "\""завершится ошибкой.
Устаревшее
val fscanf : in_channel -> ('a, 'b, 'c, 'd) scanner
Scanf.fscanf подвержена ошибкам и устарела с версии 4.03.0. Эта функция нарушает следующее инвариантное свойство модуля Scanf: для сохранения семантики сканирования, все функции сканирования, определённые в Scanf, должны читать из пользовательского канала форматированного ввода Scanf.Scanning.in_channel. Если вам нужно читать из канала ввода in_channel ic, просто определите канал форматированного ввода Scanf.Scanning.in_channel как в let ib = Scanning.from_channel ic, затем используйте Scanf.bscanf ib как обычно.val kfscanf : in_channel -> (Scanning.in_channel -> exn -> 'd) -> ('a, 'b, 'c, 'd) scanner
Scanf.kfscanf подвержена ошибкам и устарела с версии 4.03.0.
© 1995-2022 INRIA.
https://v2.ocaml.org/releases/4.14/htmlman/libref/Scanf.html