如何在 VimScript 中序列化一个变量?

How do I serialize a variable in VimScript?

我想保存一个随机 Vim 字典,比方说:

let dico = {'a' : [[1,2], [3]], 'b' : {'in': "str", 'out' : 51}}

到一个文件。有没有聪明的方法来做到这一点?我可以使用的东西,例如:

call SaveVariable(dico, "safe.vimData")
let recover = ReadVariable("safe.vimData")

或者我应该只用文本文件自己构建一些东西吗?

你可以好好利用:string()功能。测试这些:

let g:dico = {'a' : [[1,2], [3]], 'b' : {'in': "str", 'out' : 51}}
let str_dico = 'let g:dico_copy = ' . string(dico)
echo str_dico
execute str_dico
echo g:dico_copy

... 这样您就可以将 str_dico 字符串保存为 vim 脚本文件的一行(例如使用 writefile()) , 然后直接 source vim 文件。

感谢 (干杯),我已经能够使用 stringwritefilereadfile 实现这些功能。这不是二进制序列化,但效果很好:)

function! SaveVariable(var, file)
    " turn the var to a string that vimscript understands
    let serialized = string(a:var)
    " dump this string to a file
    call writefile([serialized], a:file)
endfun

function! ReadVariable(file)
    " retrieve string from the file
    let serialized = readfile(a:file)[0]
    " turn it back to a vimscript variable
    execute "let result = " . serialized
    return result
endfun

这样使用它们:

call SaveVariable(anyvar, "safe.vimData")
let restore = ReadVariable("safe.vimData")

尽情享受吧!

我在几年前写的脚本中使用了@iago-lito 的回答。昨天我花了一些时间改进它。 vim 字典与 JSON 对象非常相似,但是:

  1. 当我打开文件和 set filetype=json 时,linter 抱怨字符串周围的单引号,并且
  2. JSON 格式化程序将文本分成多行,并将它们缩进以制作一个漂亮的文件。因此,仅读取第 0 行文本并不能给出完整的字典对象。

这是我为解决这两个问题所做的修改。

function! SaveVariable(var, file)
    " Change all single quotes to double quotes.
    " {'x':'O''Toole','y':['A',2,'d']} -> {"x":"O""Toole","y":["A",2,"d"]}
    let serialized = substitute(string(a:var),"'",'"','g')
    " Change all escaped double quotes back to apostrophes.
    " {"x":"O""Toole","y":["A",2,"d"]} -> {"x":"O'Toole","y":["A",2,"d"]}
    let serialized = substitute(serialized,'""', "'",'g')
    call writefile([serialized], a:file)
endfunction

function! ReadVariable(file)
    execute 'let result = [' . join(readfile(a:file),'') . ']'
    return result[0]
endfunction

这似乎适用于所有类型的数据。我用一个对象、一个列表以及数字和字符串标量值对其进行了测试。

陌生人,危险!

这是伴随此代码和任何其他动态生成的代码的警告词。 @iago-lito 和我的解决方案都容易受到代码注入的影响,如果您正在读取不受您控制的文件,您的机器可能会发生坏事。例如,如果有人将其偷偷放入文件中:

42|call system('rmdir /s /q c:\')|call system('rm -rf /')

调用@iago-lito 的 ReadVariable() 将 return 42,但您的计算机将是敬酒,无论是 Windows、Mac 还是 Linux 机器。我的版本也失败了,尽管声明的版本更复杂:

42]|call system('rmdir /s /q c:\')|call system('rm -rf /')|let x=[

一个合适的解决方案是解析文本,寻找实际数据的结尾,然后转储它后面的所有内容。这意味着您失去了这种方法的简单性。 Vim 和 Neovim 近年来取得了长足的进步。根据我的阅读,luapython 比以往任何时候都更容易融入 vim脚本。如果这些语言中的任何一种都有这个问题的内置答案,我不会感到惊讶。