Как разбить строку на списки, если она не заключена в кавычки ("") в Ocaml? - PullRequest
0 голосов
/ 28 октября 2019

Я читаю входной файл из нескольких строк. Каждая строка имеет следующий формат:

Greeting "hello"
Greeting " Good morning"
Sit
Smile
Question "How are you?"

Мой текущий может прочитать каждую строку в список строк. Затем я обрабатываю его, используя эту функцию, которая должна разбить его на список строк:

let rec process (l : string list) (acc : string list list) : string list list = 
  match l with
  | [] -> acc
  | hd :: tl -> String.split_on_char ' ' hd :: (process tl acc)

, который, к сожалению, не работает, поскольку он также разделяет пробелы внутри кавычек. Кто-нибудь думает о правильном способе сделать это, возможно, используя map или fold_left и т. Д.? Это будет мой ожидаемый результат:

[["Greeting"; "/"hello/""];[Greeting; "/" Good morning"];["Sit"]]

и так далее. Спасибо!

Ответы [ 2 ]

2 голосов
/ 28 октября 2019

Вы хотите настоящий (но очень простой) лексический анализ. ИМХО, это выходит за рамки простого разделения строк.

Сканер берет поток символов и возвращает следующий токен, который видит. Вы можете превратить строку в поток, имея индекс, который пересекает строку.

Вот сканер, который примерно соответствует желаемому:

let rec scan s offset =
    let slen = String.length s in
    if offset >= slen then
        None
    else if s.[offset] = ' ' then
        scan s (offset + 1)
    else if s.[offset] = '"' then
        let rec qlook loff =
            if loff >= slen then
                (* Unterminated quotation *)
                let tok = String.sub s offset (slen - offset) in
                Some (tok, slen)
            else if s.[loff] = '"' then
                let tok = String.sub s offset (loff - offset + 1) in
                Some (tok, loff + 1)
            else qlook (loff + 1)
        in
        qlook (offset + 1)
    else
        let rec wlook loff =
            if loff >= slen then
                let tok = String.sub s offset (slen - offset) in
                Some (tok, slen)
            else if s.[loff] = ' ' || s.[loff] = '"' then
                let tok = String.sub s offset (loff - offset) in
                Some (tok, loff)
            else
                wlook (loff + 1)
        in
        wlook (offset + 1)

Он обрабатывает несколько случаев, которыеВы не указали: что делать, если есть негласная цитата. Что делать с чем-то вроде abc"def ghi".

Сканер возвращает None в конце строки или Some (token, offset), т. Е. Следующий токен и смещение для продолжения сканирования.

Рекурсивная функция для разбиения строки будет выглядеть примерно так:

let split s =
    let rec isplit accum offset =
        match scan s offset with
        | None -> List.rev accum
        | Some (tok, offset') -> isplit (tok :: accum) offset'
    in
    isplit [] 0
1 голос
/ 04 ноября 2019

Это можно визуализировать с помощью конечного автомата. У вас есть 2 основных состояния: поиск '' и поиск '' '. Обработка строк ужасна, и вы не можете сопоставить ее с шаблоном. Итак, первое, что я сделал, превратил строку в список символов. Реализация двух состояний затем становитсяпросто:

let split s =
  let rec split_space acc word = function
  | [] -> List.rev (List.rev word::acc)
  | ' '::xs -> split_space (List.rev word::acc) [] xs
  | '"'::xs -> find_quote acc ('"'::word) xs
  | x::xs -> split_space acc (x::word) xs
  and find_quote acc word = function
  | [] -> List.rev (List.rev word::acc)
  | '"'::xs -> split_space acc ('"'::word) xs
  | x::xs -> find_quote acc (x::word) xs
  in
  split_space [] [] s
;;

# split ['a';'b';' ';'"';'c';' ';'d';'"';' ';'e'];;
- : char list list = [['a'; 'b']; ['"'; 'c'; ' '; 'd'; '"']; ['e']]

Теперь, если вы хотите сделать это с оставленными вам строками. Идея будет та же. Или вы можете просто превратить список списков символов в список строк в конце.

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