erlang列出来自文件末尾位置的行

时间:2015-08-27 09:03:04

标签: erlang

是否有可能从最后返回x行文件的函数?该函数将采用参数定义我们想要读取的距离(在行测量中)以及我们希望从该位置返回多少行:

get_lines_file_end(IoDevice, LineNumberPositionFromEnd, LineCount) ->

实施例: 我们有30行0-29的文件

get_lines_file_end(IoDevice, -10, 10) // will return lines 20-29
get_lines_file_end(IoDevice, -20, 10) // will return lines 10-19

这个问题是我只能通过一定数量的字节来寻找文件:位置。

目的: 从上一页"页面"开始以页面方式查看大型日志文件(数百MB)。 Erlang用于javascript web使用的rest api。

此功能的用法是逐页查看整个日志文件,其中页面由x行文本表示。不需要处理日志文件或获取某些信息。

由于

2 个答案:

答案 0 :(得分:2)

要提出两点:

  1. 为了提高效率,您必须创建有关文本文件内容的元数据,以分摊相关工作。通过这种方式,您可以在创建此元数据之后使用file:position/2 来直接跳转到您需要的位。
  2. 如果这是您的用例,那么您应该以不同方式对作品进行分区。巨大的文本文件应该分解为较小的文本文件,或者(更有可能)你根本不应该使用文本文件。取决于你的目标是什么(你没有提到过;我强烈怀疑这是一个XY问题)你可能根本不想要文本而是想知道一些东西由文本表示。为了以防万一,将原始文本保存在某处可能是一个好主意,但对于数据的实际处理几乎肯定是创建符号数据(更简要地说)表示您对数据感兴趣的任何内容的更好的想法,并将其存储在数据库中,在这个数据库中,搜索,扫描,编制索引以及执行其他任何您可能需要的操作都是自然操作。
  3. 要构建有关文件的元数据,您需要执行类似于以下操作的内容:

    1> {ok, Data} = file:read_file("TheLongDarkTeaTimeOfTheSoul.txt").
    {ok,<<"Douglas Adams. The Long Dark Tea-Time of the Soul\r\n\r\n"...>>}
    2> LineEnds = binary:matches(Data, <<"\r\n">>).                   
    [{49,2},
     {51,2},
     {53,2},
     {...}|...]
    

    然后将LineEnds分别保存为文件本身的元数据。在文件数据中使用此搜索是基本的(如使用file:position/2,其中数据在换行符X处,或length(LineEnds) - X或其他任何内容。

    但这仍然很愚蠢。

    如果你想在日志文件中跳转,特别是如果你想能够找到它们中的模式,计算它们的某些方面等等,那么你几乎肯定会更好地将它们读入像Postgres这样的数据库中按行,计算行数。那时,分页成为一个微不足道的问题。

    但是,日志文件通常充满了最符合符号而非实际文本的数据,并且标记日志文件可能更好。考虑访问日志文件的情况。重复数量的访问者从有限数量的接入点(IP,设备或其他)访问任意次数。可以在数据库中单独索引和比较而非简单的每个方面。标记化本身也是微不足道的。在以后的数据分析方面,这个解决方案不仅更多更快,而且它自然有助于回答非常困难来回答有关数据内容的问题以非常直率和熟悉的方式。 ......你甚至不必丢失任何原始数据或处理的中间阶段(可能以不同的方式独立使用)。

    另外......请注意,上述所有工作都可以在Erlang中轻松实现。无论您的计算资源情况如何,编写最能充分利用硬件的解决方案当然都在掌握之中(假设您有足够的总数据,这甚至是一个问题)。

    就像很多“如何用数据Y做X?”问题,真正的答案总是围绕“你对数据的目标是什么以及为什么?”

答案 1 :(得分:0)

您可以使用file:read_line/1 function读取行,丢弃与您的范围不匹配的行:

get_lines(File, From) when From > 0 ->
    get_lines(File, file:read_line(File), From, 1).

get_lines(_File, eof, _From, _Current) ->
    [];
get_lines(File, {ok, _Line}, From, Current) when Current < From ->
    get_lines(File, file:read_line(File), From, Current + 1);
get_lines(File, {ok, Line}, From, Current) ->
    [Line|get_lines(File, file:read_line(File), From, Current + 1)];
get_lines(_IoDevice, Error, _From, _Current) ->
    Error.