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