ETS表把元组保存在内存里,而DETS提供了把Erlang元组保存到磁盘上的方法。DETS的最 大文件大小是2GB。DETS文件必须先打开才能使用,用完后还应该正确关闭。如果没有正确关 闭,它们就会在下次打开时自动进行修复。因为修复可能会花很长一段时间,所以先正确关闭它 们再结束程序是很重要的。 DETS表有着和ETS表不同的共享属性。DETS表在打开时必须赋予一个全局名称。如果两个 或更多本地进程用相同的名称和选项打开某个DETS表,它们就会共享这个表。这个表会一直处于打开状态,直到所有进程都关闭它(或者崩溃)。
接下来我们将创建一个基于磁盘的表,它将把文件名映射到整数上,反之亦然。我们将定义函数 filename2index和它的逆函数index2filename。 为了实现这个索引,我们将创建一个DETS表,然后用三种不同类型的元组填充它。
{free, N}
N是表里的第一个空白索引。当我们在表里输入一个新文件名时,就会指派索引N给它。
{FileNameBin, K}
FileNameBin(一个二进制型)被指派了索引K。
{K, FileNameBin}
K(一个整数)代表文件FilenameBin。
请注意,每个新添加的文件都会在表里增加两个条目:一个File → Index条目和一个逆向 的Index → Filename条目。这是出于效率的原因。当ETS或DETS表建立时,元组里只有一个元素充当键的角色。匹配某个非键元组元素虽然可行,但非常低效,因为它涉及对整个表进行搜索。当整个表都在磁盘上时,这个操作的代价尤其高昂。
现在来编写这个程序。首先编写打开和关闭DETS表的函数,这个表用来保存所有的文件名。
open(File) ->
io:format ("detsopened:~p~n",[File]),
Bool = filelib:is_file(File),
case dets:open_file(?MODULE,[{file, File}]) of
{ok, ?MODULE} ->
case Bool of
true -> void;
false -> ok = dets:insert(?MODULE, {free, 1})
end,
true;
{error, Reason} ->
io:format("cannot open dets table-n" ),
exit({eDetsopen, File, Reason})
end.
close() -> dets:close(?MODULE).
open的代码会自动初始化DETS表,方法是在创建新表时插入{free, 1}元组。如果File存 在,filelib:is_file(File)就会返回true,否则返回false。
打开文件之后,向表里插入一个新文件名就很简单了。它是由filename2index调用产生的副作用实现的。如果文件名在表里,就会返回它的索引,否则就会生成一个新索引并更新表,而 这次更新会使用三个元组。
filename2index(FileName) when is_binary(FileName) ->
case dets:lookup(?MODULE, FileName) of
[] ->
[{_, Free}] = dets:lookup(?MODULE, free),
ok = dets:insert(?MODULE, [{Free, FileName}, {FileName, Free}, {free, Free+1}]),
Free;
[{_, N}] ->
N
end.
把索引转换成文件名非常简单。
index2filename(Index) when is_integer(Index) ->
case dets:lookup(?MODULE, Index) of
[] -> error;
[{_, Bin}] -> Bin
end.
我们测试一下
发现创建了应该file文件