Spec-Zone.ru › OCaml 4.14

Модуль 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 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.

  • 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

Spec-Zone.ru

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