Spec-Zone.ru › OCaml

Модуль 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 f
применяет f ко всем аргументам, заданным форматной строкой 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API