以下是问题的描述:
我在目录中有大量的小日志文件,假设:
yyyy-mm-dd.log
,例如:2013-01-01.log,2013-01-02.log。现在我必须为每个文件中的每一行添加一个行号,并且行号是累积的,在文件夹中的所有文件(文件按时间戳排序)之间传播。例如:
覆盖所有文件以包含行号。
约束是:
经过思考和搜索,这是我所想到的最好的solution。 The code有点儿 很长,所以我只简单介绍一下每一步:
同时计算每个文件的行数并将映射保存到ConcurrentSkipListMap
,密钥是文件名,值是文件的行数,密钥是有序的
通过遍历ConcurrentSkipListMap
计算每个文件的起始行号,例如,2013-01-01.log的起始行号和行数分别为1和1500,然后开始2013-01-02.log的行号是1501。
在每个文件的每一行前面添加行号:使用BufferedReader
逐行读取每个文件,添加行号,然后使用BufferedWriter
写入相应的tmp文件。创建一个线程池并同时处理。
使用线程池同时将所有tmp文件重命名为原始名称。
我已经在我的MBP上测试了该程序,步骤1和步骤3是预期的瓶颈。 您有更好的解决方案,或者我的解决方案的一些优化?提前谢谢!
答案 0 :(得分:1)
不确定这些问题是否符合Q& A的SO模型,但我尝试了一些答案。
事实1)鉴于1M文件和100MB限制,几乎没有办法同时保存内存中所有文件的信息。除了可能通过像我们在C中编程的过去那样做一些小小的摆弄。
事实2)我没有办法绕过读取所有文件一次来计算行号,然后重写所有文件,这意味着再次阅读它们。
A)这是一个功课问题吗?可能有一种方法可以在Java 7或8中一个接一个地从一个文件夹中生成文件名,但我不知道它。如果有,请使用它。如果没有,您可能需要生成文件名而不是列出它们。这将要求您可以插入开始日期和结束日期作为输入。不确定这是否可行。
B)鉴于存在一个惰性Iterator<File>
,无论是从jdk到列表文件还是自我实现来生成文件名,都要获得N个来将工作划分为N个线程。
C)现在每个线程都会处理它的文件片段,读取它们并仅保留其片段的总行数。
D)从每个切片的总计算出每个切片的起始编号。
E)再次在N个线程上分配迭代器以进行行编号。在写入tmp文件后立即重命名,不要等待所有内容完成,不必再次遍历所有文件。
在每个时间点,内存中保存的信息都很小:每个线程一个文件名,整个切片上的行数,正在读取的文件的当前行。如果N不是非常大,那么100MB就足够了。
编辑:Some say Files.find()
已经懒洋洋地填充,但我无法轻易找到它背后的代码(Java 8中的一些DirectoryStream
),看看懒惰是否只与读取相关一次一个文件夹的完整内容,或者是否确实一次读取一个文件名。或者这是否取决于所使用的文件系统。