用java读取hdfs文件并用regex匹配多行块

ewm0tg9j  于 2021-06-02  发布在  Hadoop
关注(0)|答案(2)|浏览(361)

我正在使用日志分析工具。
我在hadoop中使用yarn日志聚合函数。当我这样做的时候,hadoop日志文件非常大,以至于一些api方法无法将文件的内容完全读取到内存中。
我想在第一行包含字符串的文件中匹配多行块 [map] 最后一行包含 [\map] -我想我可以用正则表达式来做这个。常用的 BufferedReader 不能满足我的要求。
我的问题是:是否有其他方法逐行检查文件,检查那些与我的regex匹配的文件?
p、 我真的不想分割成多个较小的文件来处理,因为我担心这会导致一些匹配的内容找不到,因为我可能会分割在一个匹配块的中间文件。
下面是日志文件的片段-我想要 [MAP] 以及 [/MAP] :

2015-04-16 20:30:09,240 INFO [main] org.apache.hadoop.hive.ql.exec.MapOperator: dump TS struct
2015-04-16 20:30:09,240 INFO [main] org.apache.hadoop.hive.ql.exec.mr.ExecMapper: 

    [MAP]Id =4
      [Children]
        [TS]Id =2
          [Children]
            [RS]Id =3
              [Parent]Id = 2 null[\Parent]
            [\RS]
         [\Children]
         [Parent>Id = 4 null[\Parent]
       [\TS]
      [\Children]
    [\MAP]

2015-04-16 20:30:09,241 INFO [main] org.apache.hadoop.hive.ql.exec.MapOperator: Initializing Self 4 MAP
2015-04-16 20:30:09,242 INFO [main] org.apache.hadoop.hive.ql.exec.TableScanOperator: Initializing Self 2 TS
2015-04-16 20:30:09,242 INFO [main] org.apache.hadoop.hive.ql.exec.TableScanOperator: Operator 2 TS initialized
um6iljoc

um6iljoc1#

n、 b.根据评论中的澄清进行编辑
使用regex可以找到你的多行块-你当然可以写一个regex来匹配它们。 .*\[MAP\]((?s).*)\[\\MAP\] -注意,在java中,还必须避开所有 \ 人物之类的 (?s) 允许 . 匹配换行符的字符,即。

String mapBlockRegex = ".*\\[MAP\\]((?s).*)\\[\\\\MAP\\]";`

但是,正如您所强调的,如果文件无法放入内存,则会产生困难,拆分它也会有一些困难。
我将提出一个不同的想法-逐行扫描文件,并使用状态变量来指示是否在块中。基本算法如下
匹配块的开头时,将状态变量设置为true。
当state为true时,将文本附加到 StringBuilder 匹配块的结尾时,将状态变量设置为false并使用 String 你已经建立了,例如,输出到文件,到控制台或使用它的编程。

java解决方案

我将建议一种实现上述功能的方法—使用 Scanner -它一行一行地穿过一条溪流,沿途丢弃它们,从而避免 OutOfMemoryError . 注意这个代码可以抛出异常-我已经抛出了它们,但是您可以将它们放入 try..catch..finally 阻止。还要注意的是 Scanner 吞咽io异常,但正如医生所说,如果这对你很重要:
可以通过ioexception()方法检索基础readable函数抛出的最新ioexception。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;

public class LogScanner
{

    public static void main(String[] args) throws FileNotFoundException
    {
        FileInputStream inputStream = null;
        Scanner sc = null;

        String path = "D:\\hadoopTest.log";
        String blockStart= ".*\\[MAP\\].*";
        String blockEnd = ".*\\[\\\\MAP\\].*";
        boolean inBlock = false;
        StringBuilder block = null;

        inputStream = new FileInputStream(path);
        sc = new Scanner(inputStream, "UTF-8");
        while (sc.hasNextLine()) {
            String line = sc.nextLine();
            if (line.matches(blockStart)) {
                inBlock = true;
                block = new StringBuilder();
            }

            if (inBlock) {
                block.append(line);
                block.append("\n");
            }

            if (line.matches(blockEnd)) {
                inBlock = false;
                String completeBlock = block.toString();
                System.out.println(completeBlock);
                // I'm outputting the blockto stdout, you could append to a file\whatever.
            }
        }

        sc.close();
    }
}

警告:您的文件可能有一些特性,如果不进行一些修改,这些特性将无法工作。如果可以嵌套 [map] 那么是街区 inBlock 需要是一个int,如果匹配块开始,则递增,如果匹配结束,则递减-对于任何 inblock > 0 只有在 inBlock 归零。

命令行拆分,在单行上查找匹配项

如果您是在每行的基础上搜索,并且匹配项保证在一行上,那么只要分割只发生在完整行的末尾,分割就可以了。
在这种情况下,可以使用命令行拆分文件。如果您使用的是linux(或者,我认为,任何*nix),那么可以使用split命令,例如。

split --lines=75000

在这个问答中有更多的细节
在windows上,我知道没有一个等效的命令,但是您可以安装类似的命令,例如gnucoreutils for windows或7-zip。注意:我从来没有用过这些来分裂。

inkz8wg9

inkz8wg92#

您可以使用javanio包来代替缓冲读取器,这与缓冲读取器相比非常快

相关问题