我有几个中等大小(1到5 GB)的二进制文件,我想阅读并使用Erlang进行处理。
每个文件都有不同大小的记录,即一个记录为200 kb,但其他记录的大小可能为800 kb。通过读取记录的前几个字节可以获得记录大小。由于它是二进制文件,因此两条记录之间没有分隔符。
要处理这些文件,我们可以编写多线程程序,但为了获得乐趣,我想到使用Erlang并行处理文件。
我是Erlang的新手,所以我不知道如何将文件分成块并将这些块传递给Erlang进程。
任何人都可以提出一些想法吗?
答案 0 :(得分:2)
我已经在其他编程语言中多次这样做了,这在Erlang中是一个有趣的学习练习。
基本策略是告诉每个进程它的起始字节偏移量,记录大小和要读取的记录数。 (如果在一个文件中有不同大小的记录,则可以传递记录大小列表,如[200, 800, 800, 800, 800, 200, 100]
)。工作人员独立处理他们的块并将结果返回给父级。
我认为你很容易弄清楚自己。您需要查看erlang:spawn
,file:open
,file:read
和file:position
作为主要内容。但是如果你想要破坏者,这里是我实现的一个模块,它从二进制文件中读取数字并使用多个进程查找平均值。 (我不是Erlang专家,所以可能有更好的方法来做到这一点。)
-module(average).
-export([write_file/3, read_file/4]).
-export([read_file_worker/5]).
write_file(Filename, BlockSize, ListOfNumbers) ->
BS = BlockSize*8,
BinData = [<<X:BS>> || X <- ListOfNumbers],
{ok, IoDevice} = file:open(Filename, [write, raw, binary]),
file:write(IoDevice, BinData),
file:close(IoDevice).
read_file(Filename, BlocksPerProcess, BlockSize, TotalBlockCount) ->
{ok, SpawnCount} = read_file_spawner(Filename, BlocksPerProcess, BlockSize, TotalBlockCount, 0, 0),
{ok, Sum} = read_file_listener(SpawnCount, 0),
io:format("Total sum: ~p~nNumbers seen: ~p~nAverage: ~p~n", [Sum, TotalBlockCount, Sum/TotalBlockCount]).
read_file_spawner(Filename, BlocksPerProcess, BlockSize, TotalBlockCount, BlockOffset, SpawnCount) when BlockOffset < TotalBlockCount ->
Offset = BlockOffset * BlockSize,
MaxBlocks = min(BlocksPerProcess, TotalBlockCount - BlockOffset),
spawn(?MODULE, read_file_worker, [self(), Filename, Offset, BlockSize, MaxBlocks]),
read_file_spawner(Filename, BlocksPerProcess, BlockSize, TotalBlockCount, BlockOffset + BlocksPerProcess, SpawnCount + 1);
read_file_spawner(_Filename, _BlocksPerProcess, _BlockSize, _TotalBlockCount, _BlockOffset, SpawnCount) ->
{ok, SpawnCount}.
read_file_listener(0, Accum) ->
{ok, Accum};
read_file_listener(SpawnCount, Accum) ->
receive
{ok, Number} ->
io:format("Got ~p~n", [Number]),
read_file_listener(SpawnCount - 1, Accum + Number)
end.
read_file_worker(MasterPid, Filename, Offset, BlockSize, MaxBlocks) ->
{ok, IoDevice} = file:open(Filename, [read, raw, binary]),
{ok, Offset} = file:position(IoDevice, {bof, Offset}),
{ok, Sum} = read_file_worker_loop(IoDevice, BlockSize, 0, MaxBlocks, 0),
MasterPid ! {ok, Sum}.
read_file_worker_loop(IoDevice, BlockSize, BlocksRead, MaxBlocks, Accum) when BlocksRead < MaxBlocks ->
{ok, BinData} = file:read(IoDevice, BlockSize),
Number = binary:decode_unsigned(BinData),
read_file_worker_loop(IoDevice, BlockSize, BlocksRead + 1, MaxBlocks, Accum + Number);
read_file_worker_loop(_IoDevice, _BlockSize, _BlocksRead, _MaxBlocks, Accum) ->
{ok, Accum}.
在这里它正在运行,使用5个进程从文件中读取10个数字:
12> Numbers = [1,1,2,4,6,10,10,1,0,500].
[1,1,2,4,6,10,10,1,0,500]
13> average:write_file("/tmp/test.binary", 32, Numbers).
ok
14> average:read_file("/tmp/file.binary", 2, 32, length(Numbers)).
Got 500
Got 11
Got 6
Got 16
Got 2
Total sum: 535
Numbers seen: 10
Average: 53.5
ok
答案 1 :(得分:0)
为什么不为每个文件启动进程?我看不出将小文件拆分成块的价值。你的文件已经很小了。这也可以在许多机器上并行化。