我不确定解决此问题的最佳方法,所以我想问一下。我有这样一行:
NAME="/dev/sda" TYPE="disk" MODEL="KINGSTON SV300S3"
(来自lsblk,有几个选项),我想尽可能简单地提取每个字段。是的,我知道lsblk有一个非常不错的--json,但是很遗憾,这是我无法使用的最新功能,我们还有一些相当老的服务器仍在生产中。
也许将Str与正则表达式一起使用?谷歌似乎说很多,我从来没有使用过,但是恐怕对于像这样的一些变量来说可能有点沉重? 我已经尝试过使用String.split_on_char和String.slice,但是当模型包含空格时,它开始变得复杂,String.split_on_char当然不会忽略双引号之间的空格。
答案 0 :(得分:3)
尽管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")]
答案 1 :(得分:3)
对于像这样的简单格式,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”)
符合预期。
答案 2 :(得分:0)
知道了:
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