Модуль Scanf
module Scanf: sig .. end
Функции форматированного ввода.
- Предупреждение unsynchronized_access. Несинхронизированные обращения к Scanning.in_channel являются программно ошибочными.
Введение
Функциональный ввод с использованием строк формата
Модуль 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 полностью проверяется во время компиляции.
Несинхронизированные обращения
Несинхронизированные обращения к Scanf.Scanning.in_channel могут привести к недопустимому состоянию Scanf.Scanning.in_channel. Таким образом, одновременные обращения к Scanf.Scanning.in_channel должны быть синхронизированы (например, с помощью Mutex.t).
Канал форматированного ввода
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
type ('a, 'b, 'c, 'd) scanner_opt = ('a, Scanning.in_channel, 'b, 'c, 'a -> 'd option, 'd) format6 -> 'c 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, указанным в строке формата.
val bscanf_opt : Scanning.in_channel -> ('a, 'b, 'c, 'd) scanner_opt
Аналогично Scanf.bscanf, но возвращает None в случае ошибки сканирования.
- Since 5.0
Описание строки формата
Строка формата — это строка символов, которая содержит три типа объектов:
- простые символы, которые просто сопоставляются с символами ввода (с особым случаем для пробела и символа новой строки, см. Символ пробела в строках формата),
- спецификации преобразования, каждая из которых вызывает чтение и преобразование одного аргумента для функции
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 sscanf_opt : string -> ('a, 'b, 'c, 'd) scanner_opt
То же, что и Scanf.sscanf, но возвращает None в случае неудачного сканирования.
- Since 5.0
val scanf : ('a, 'b, 'c, 'd) scanner
То же, что и Scanf.bscanf, но читает из предопределённого форматированного канала ввода Scanf.Scanning.stdin, который подключён к stdin.
val scanf_opt : ('a, 'b, 'c, 'd) scanner_opt
То же, что и Scanf.scanf, но возвращает None в случае неудачного сканирования.
- Since 5.0
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 с последовательностями экранирования (согласно лексическим соглашениям OCaml) заменёнными соответствующими специальными символами. Более точно, Scanf.unescaped обладает следующим свойством: для всех строк s, Scanf.unescaped (String.escaped s) = s.
Всегда возвращает копию аргумента, даже если в аргументе нет последовательностей экранирования.
- Since 4.00.0
-
Вызывает
Scan_failureеслиsне правильно экранирована (т.е.sимеет неверные последовательности экранирования или специальные символы, которые не правильно экранированы). Например,Scanf.unescaped "\""завершится ошибкой.
© 1995-2022 INRIA.
https://v2.ocaml.org/releases/5.0/htmlman/libref/Scanf.html