Простой разбор строк в Ocaml - PullRequest
       8

Простой разбор строк в Ocaml

0 голосов
/ 30 августа 2018

Я не уверен, как лучше подойти к этому, поэтому я решил спросить. У меня есть такая строка:

NAME="/dev/sda" TYPE="disk" MODEL="KINGSTON SV300S3"

(получено из lsblk с несколькими опциями), и я хотел бы извлечь каждое поле как можно проще. Да, я знаю, что у lsblk есть очень хороший --json, но, к сожалению, это недавнее дополнение, которое я не могу использовать, у нас есть несколько довольно старых серверов, которые все еще работают.

Может быть, использовать Str с некоторым регулярным выражением? Google, кажется, много говорит о менгире, я никогда не использовал его, но боюсь, что это может быть немного тяжелым только для нескольких таких переменных? Я пытался использовать String.split_on_char и String.slice, но это становится все сложнее, когда модель содержит пробелы, String.split_on_char, конечно, не игнорирует пробелы между двойными кавычками.

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

Понял:

let re = Str.regexp "NAME=\"\\(.*\\)\" TYPE=\"\\(.*\\)\" MODEL=\"\\(.*\\)\"" in
  match Str.string_match re line 0 with
  | false -> [`Null]
  | true  ->
     let name = Str.matched_group 1 line in
     let typ = Str.matched_group 2 line in
     let model = Str.matched_group 3 line in
     Printf.printf "%s, %s, %s\n" name typ model
0 голосов
/ 30 августа 2018

Для такого простого формата модуль Scanf может быть жизнеспособной альтернативой:

let extract s = Scanf.sscanf s "NAME=%S TYPE=%S MODEL=%S" (fun x y z -> x, y ,z);;
;; extract {|NAME="/dev/sda" TYPE="disk" MODEL="KINGSTON SV300S3"|}

выходы

("/ dev / sda", "диск", "KINGSTON SV300S3")

как и ожидалось.

0 голосов
/ 30 августа 2018

В то время как Str, вероятно, мог бы добиться цели, менее известный Genlex модуль из стандартной библиотеки может оказаться весьма полезным для не слишком тяжелых манипуляций со строками, по крайней мере для форматов, которые более или менее повинуйтесь лексическому соглашению OCaml. По сути, он превратит ваш поток char в поток токенов, который вы сможете анализировать намного проще. Я полагаю, что полный формат вывода lsblk может потребовать некоторых уточнений, но для вашего примера достаточно следующего:

let lexer = Genlex.make_lexer [ "=" ]

let test = "NAME=\"/dev/sda\" TYPE=\"disk\" MODEL=\"KINGSTON SV300S3\""
let test_stream = Stream.of_string test
let test_stream_token = lexer test_stream

let info =
  let l = ref [] in
  try
    while true do
      let kw = Stream.next test_stream_token in
      let eq = Stream.next test_stream_token in
      let v = Stream.next test_stream_token in
      let kw =
        match kw with Ident s -> s | _ -> failwith "Unrecognized pattern"
      in
      let () = match eq with Kwd "=" -> () | _ -> failwith "Expected '='" in
      let v = match v with String s -> s | _ -> failwith "Expected string" in
      l:=(kw,v)::!l
    done;
    assert false
  with Stream.Failure -> List.rev !l

По сути, основной цикл считает, что информация, содержащаяся во входных данных, представляет собой последовательность элементов вида <key>="<value>", разложенных на три токена с помощью сгенерированного Genlex лексера.

Результат: [("NAME", "/dev/sda"); ("TYPE", "disk"); ("MODEL", "KINGSTON SV300S3")]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...