我不确定最好的办法,所以我想我会问。我有这样一条线:
NAME="/dev/sda" TYPE="disk" MODEL="KINGSTON SV300S3"
(从lsblk获得了一些选项)我想尽可能简单地提取每个字段。是的,我知道lsblk有一个非常好的-json,但遗憾的是最近我不能使用的一个新增功能,我们还有一些很老的服务器还在生产中。
也许使用Str与一些正则表达式? Google似乎对menhir说了很多,我从来没有使用过它,但我担心这样的变量可能会有点沉重吗?我尝试过使用String.split_on_char和String.slice,但是当模型包含空格时它开始变得复杂,String.split_on_char当然不会忽略双引号之间的空格。
虽然Str
可能可以做到这一点,但标准库中鲜为人知的Genlex
module可以非常方便地进行不太重的字符串操作,至少对于那些或多或少遵循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")]
对于像这样的简单格式,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 / surrender”,“disk”,“KINGSTON SV300S3”)
正如所料。
得到它了 :
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