Модуль 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.
- С момента 3.10
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 в случае неудачи сканирования.
- С момента 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
Чтение строк формата из ввода
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
-
Вызывает
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
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
-
Вызывает
Scan_failureеслиs, рассматриваемая как строка формата, не имеет того же типа, что иfmt.
val unescaped : string -> string
unescaped s возвращает копию s с последовательностями экранирования (согласно лексическим соглашениям OCaml) заменёнными соответствующими специальными символами. Более точно, Scanf.unescaped обладает следующим свойством: для всех строк s, Scanf.unescaped (String.escaped s) = s.
Всегда возвращает копию аргумента, даже если в аргументе нет последовательностей экранирования.
- Since 4.00
-
Вызывает
Scan_failureеслиsне правильно экранирована (т.е.sсодержит недействительные последовательности экранирования или специальные символы, которые не правильно экранированы). Например,Scanf.unescaped "\""завершится с ошибкой.
© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/api/Scanf.html