Java使用正则表达式提取字段分隔的子字符串

Posted

技术标签:

【中文标题】Java使用正则表达式提取字段分隔的子字符串【英文标题】:Java extract field delimited substring using regex 【发布时间】:2013-10-14 22:17:03 【问题描述】:

如何使用正则表达式从系统日志消息中提取程序名?我有一个 Java 流处理模块,它接受正则表达式来处理 syslog 消息。

日志行可能是:

2013-10-14T22:05:29+00:00 hostname sshd[6359]: Connection closed by 192.168.1.10
2013-10-14T22:05:29+00:00 hostname sshd:3322 Connection closed by 192.168.1.10
2013-10-14T22:05:29+00:00 hostname sshd/6359 Connection closed by 192.168.1.10
2013-10-14T22:05:29+00:00 hostname sshd Connection closed by 192.168.1.10
2013-10-14T22:05:29+00:00 hostname SSHD[1133] Connection closed by 192.168.1.10
2013-10-14T22:05:29+00:00 hostname SSH.D[6359]: Connection closed by 192.168.1.10

字符串提取过程应该是:取第三个以空格分隔的子字符串,提取以[:/或空格结尾的子字符串

所以在前四个日志样本中,提取的字符串将是sshd、第五个SSHD 和第六个SSH.D。这可以通过正则表达式实现吗?

编辑:

我尝试的是((?:[A-Za-z][A-Za-z0-9_.-]+)),它似乎可以工作,但老实说,我修改了一个示例正则表达式并使用在线工具对其进行调整,直到它适合我的用例,但我不确定它是如何工作的。

【问题讨论】:

是的。这是可能的。你试过什么? 我尝试的是“((?:[A-Za-z][A-Za-z0-9_.-]+))”,它似乎有效,但老实说,我修改了一个示例正则表达式并使用在线工具对其进行调整,直到它适合我的用例,但我不确定它是如何工作的。 【参考方案1】:

split 应该可以胜任:

String token = data.split(" +")[2].split("[\\[:/]")[0];

【讨论】:

我正在将水槽配置中的正则表达式传递给正则表达式拦截器,因此我无法使用 Java 库/函数。 您将问题标记为 Java 并且不能使用 Java 最常见的 String 类方法? 我将它标记为 Java,因为我认为正则表达式需要与 Java 兼容。我不认为 unix shell 或 Perl 正则表达式语法适用于 Java 1:1。【参考方案2】:

试试这样的:

String str = line.split(" ")[2].replaceAll("(.+)(\\[|\\:|\\/).+", "$1");

没有测试过。

【讨论】:

我正在将水槽配置中的正则表达式传递给正则表达式拦截器,因此我无法使用 Java 库/函数。【参考方案3】:

我认为您正在寻找的正则表达式是:

String regex = "([^\\[:/]+).*";

.* 表示匹配 0 个或多个任意字符。在点星 ().* 前面放置一对括号会创建一个可以从 Matcher 中选择的组。由于它是第一组括号,因此它由组号 1 引用。括号内是一个表达式,它匹配一个或多个否定字符类[^]+,其中包含 OP 中指定的字符,特别是“[”, “:”和“/”字符。

这是一个测试结果的示例应用程序:

package com.stackexchange.***;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Question19370191 
    public static void main(String[] args) 
        String regex = "([^\\[:/]+).*";
        Pattern pattern = Pattern.compile(regex);

        List<String> lines = new ArrayList<>();
        lines.add("2013-10-14T22:05:29+00:00 hostname sshd[6359]: Connection closed by 192.168.1.10");
        lines.add("2013-10-14T22:05:29+00:00 hostname sshd:3322 Connection closed by 192.168.1.10");
        lines.add("2013-10-14T22:05:29+00:00 hostname sshd/6359 Connection closed by 192.168.1.10");
        lines.add("2013-10-14T22:05:29+00:00 hostname sshd Connection closed by 192.168.1.10");
        lines.add("2013-10-14T22:05:29+00:00 hostname SSHD[1133] Connection closed by 192.168.1.10");
        lines.add("2013-10-14T22:05:29+00:00 hostname SSH.D[6359]: Connection closed by 192.168.1.10");

        for(String line : lines) 
            String field = line.split("\\s")[2];
            String extraction = "";
            Matcher matcher = pattern.matcher(field);
            if(matcher.matches()) 
                extraction = matcher.group(1);
            

            System.out.println(String.format("Field \"%-12s\" Extraction \"%s\"", field, extraction));
        
    

它输出以下内容:

Field "sshd[6359]: " Extraction "sshd"
Field "sshd:3322   " Extraction "sshd"
Field "sshd/6359   " Extraction "sshd"
Field "sshd        " Extraction "sshd"
Field "SSHD[1133]  " Extraction "SSHD"
Field "SSH.D[6359]:" Extraction "SSH.D"

【讨论】:

我将正则表达式作为配置传递给另一个模块,因此无法使用拆分。 这是否意味着每个输入行实际上来自 InputStream 而不是 List 我想是的。正则表达式作为配置传递给水槽代理,以与水槽的正则表达式拦截器一起使用。【参考方案4】:

如果您的示例数据与您提供的完全一样:

(?:.+?\s)2([\w\.]+).+$

解释:

(?:.+?\s)2...匹配第二个空格

([^\s[:/]+)...匹配任何不是''、':'或'/'的东西

.+$...匹配 EOL

你想要的将在捕获组\1

【讨论】:

对我不起作用。如果我把你的正则表达式和一个示例日志行在这里尝试一下:java-regex-tester.appspot.com - 你的正则表达式匹配整行。

以上是关于Java使用正则表达式提取字段分隔的子字符串的主要内容,如果未能解决你的问题,请参考以下文章

基于分隔符提取字符串的正则表达式

mysql 中用正则表达式如何取一个字符串中指定的字段,

正则表达式从字符串中提取用逗号分隔的字符串

CoreData NSPredicate MATCHES 正则表达式

使用正则表达式提取部分字符串的 Hive 查询

JAVA正则表达式怎么匹配所有符合要求的子字符串