1

您好我正在尝试编写一个将一些文本转换为明确定义的标记的类。

字符串有点类似于以下代码:(brown) "fox" 'c';. 我想得到的是(来自 Scanner 的令牌或( , brown , ) , "fox" , 'c' , ;分割后的数组,我认为两者都可以正常工作)(因为它们是潜在的令牌),其中包括:

  • '带和的引用文本"
  • 带或不带小数点的数字
  • 括号、大括号、分号、等号、锐利、||、<=、&&

目前我正在使用扫描仪进行此操作,我遇到了一些问题,即分隔符无法单独给我 () 等,所以我使用了以下分隔符\s+|(?=[;\{\}\(\)]|\b),现在我会得到"它,也可以'作为单独的标记ans 我真的很想避免它,我尝试添加一些负面的前瞻性,"但没有运气。

我尝试使用 StreamTokenizer 但它没有保留不同的引号..

PS我确实搜索了该网站并尝试用谷歌搜索它,但即使有很多与扫描仪相关/正则表达式相关的问题,我也找不到可以解决我的问题的东西。

编辑1: 到目前为止,我想出的\s+|^|(?=[;{}()])|(?<![.\-/'"])(?=\b)(?![.\-/'"]) 可能还不够清楚,但是当我有类似的事情时:

"foo";'bar')(;{

gray fox=-56565.4546;

foo boo="hello"{

我想得到:

"foo", ;, 'bar', ), (, ;,{

gray, fox, =, -56565.4546,;

foo, boo, =, "hello",{

但相反,我有:

"foo", ;'bar', ), (, ;,{

gray, fox, =-56565.4546,;

foo, boo, ="hello",{

=请注意,当在 the和其余部分之间有空格时,例如 :gray fox = -56565.4546;会导致:

gray, fox, =, -56565.4546,;

我对上述正则表达式所做的是:

Scanner scanner = new Scanner(line);
    scanner.useDelimiter(MY_MENTIONED_REGEX_HERE);
    while (scanner.hasNext()) {
       System.out.println("Got: `" + scanner.next() +"`");
       //Some work here

}
4

4 回答 4

3

描述

既然您正在寻找所有可能包含小数点的字母数字文本,为什么不直接“忽略”分隔符?以下正则表达式将从您的输入字符串中提取所有带小数点的字母数字块。这是有效的,因为您的示例文本是:

"foo";'bar')(;{
gray fox=-56565.4546;
foo boo="hello"{

正则表达式:(?:(["']?)[-]?[a-z0-9-.]*\1|(?<=[^a-z0-9])[^a-z0-9](?=(?:[^a-z0-9]|$))|(?<=[a-z0-9"'])[^a-z0-9"'](?=(?:[^a-z0-9]|['"]|$)))

在此处输入图像描述

概括

正则表达式具有三个路径,它们是:

  1. (["']?)[-]?[a-z0-9-.]*\1捕获一个开引号,如果存在则加上一个减号,然后是一些文本或数字,这将一直持续到它到达闭引号。这将捕获任何带有小数点的文本或数字。这些数字未经验证,因此12.32.1会匹配。如果您的输入文本还包含以加号为前缀的数字,则更[-]改为[+-].
  2. (?<=[^a-z0-9])[^a-z0-9](?=(?:[^a-z0-9]|$))如果前一个字符是符号,则向后查找非字母数字,并且此字符是符号,下一个字符也是符号或字符串结尾,然后获取当前符号。这将捕获任何非引号的自由浮动符号,或一行中的多个符号,例如)(;{.
  3. (?<=[a-z0-9"'])[^a-z0-9"'](?=(?:[^a-z0-9]|['"]|$)))如果当前字符不是字母数字或引号,则向后查找字母数字或引号符号,并向前查找非字母数字、非引号或行尾。这将捕获引用后的任何符号,这些符号不会被先前的表达式捕获,例如{after "Hello"

完整说明

  • (?: 开始一个非组捕获语句。在这个组中,每个替代项由一个或|字符 分隔
    1. 第一种选择:(["']?)[-]?[a-z0-9-.]*\1
      • 第一捕获组(["']?)
      • 字符类["']1 到 0 次匹配以下字符之一:"'
      • 字符类[-]1 到 0 次匹配以下字符之一:-
      • Char 类[a-z0-9-.]无限到 0 次匹配以下字符之一:a-z0-9-.
      • \1匹配保存在 BackRef 1 中的文本
    2. 第二种选择:(?<=[^a-z0-9])[^a-z0-9](?=(?:[^a-z0-9]|$))
      • (?<=[^a-z0-9])积极的向后看
      • 否定的 char 类[^a-z0-9]匹配任何字符,除了:a-z0-9
      • 否定的 char 类[^a-z0-9]匹配任何字符,除了:a-z0-9
      • (?=(?:[^a-z0-9]|$))正向 LookAhead,每个子选项都由一个 or|字符分隔
      • 团体(?:[^a-z0-9]|$)
      • 第一种选择:[^a-z0-9]
      • 否定的 char 类[^a-z0-9]匹配任何字符,除了:a-z0-9
      • 第二种选择: $End of string
    3. 第三种选择:(?<=[a-z0-9"'])[^a-z0-9"'](?=(?:[^a-z0-9]|['"]|$))
      • (?<=[a-z0-9"'])积极的向后看
      • Char 类[a-z0-9"']匹配以下字符之一:a-z0-9"'
      • 否定的 char 类[^a-z0-9"']匹配任何字符,除了:a-z0-9"'
      • (?=(?:[^a-z0-9]|['"]|$))正向 LookAhead,每个子选项都由一个 or|字符分隔
      • 团体(?:[^a-z0-9]|['"]|$)
      • 第一种选择:[^a-z0-9]
      • 否定的 char 类[^a-z0-9]匹配任何字符,除了:a-z0-9
      • 第二种选择:['"]
      • Char 类['"]匹配以下字符之一:'"
      • 第三种选择: $End of string
  • )结束非组捕获语句

团体

第 0 组获取整个匹配的字符串,而第 1 组获取引号分隔符(如果存在)以确保它匹配紧引号。

Java 代码示例:

请注意,数组中的一些空值来自换行符,还有一些是从表达式中引入的。您可以应用表达式和一些基本逻辑来确保您的输出数组只有非空值。

import java.util.regex.Pattern;
import java.util.regex.Matcher;
class Module1{
  public static void main(String[] asd){
  String sourcestring = "\"foo\";'bar')(;{
gray fox=-56565.4546;
foo boo=\"hello\"{";
  Pattern re = Pattern.compile("(?:(["']?)[-]?[a-z0-9-.]*\1|(?<=[^a-z0-9])[^a-z0-9](?=(?:[^a-z0-9]|$))|(?<=[a-z0-9"'])[^a-z0-9"'](?=(?:[^a-z0-9]|['"]|$)))",Pattern.CASE_INSENSITIVE);
  Matcher m = re.matcher(sourcestring);
  int mIdx = 0;
    while (m.find()){
      for( int groupIdx = 0; groupIdx < m.groupCount()+1; groupIdx++ ){
        System.out.println( "[" + mIdx + "][" + groupIdx + "] = " + m.group(groupIdx));
      }
      mIdx++;
    }
  }
}

 $matches Array:
(
    [0] => Array
        (
            [0] => "foo"
            [1] => 
            [2] => ;
            [3] => 'bar'
            [4] => 
            [5] => )
            [6] => 
            [7] => (
            [8] => 
            [9] => ;
            [10] => 
            [11] => {
            [12] => 
            [13] => 
            [14] => 
            [15] => gray
            [16] => 
            [17] => fox
            [18] => 
            [19] => =
            [20] => -56565.4546
            [21] => 
            [22] => ;
            [23] => 
            [24] => 
            [25] => 
            [26] => foo
            [27] => 
            [28] => boo
            [29] => 
            [30] => =
            [31] => "hello"
            [32] => 
            [33] => {
            [34] => 
        )

    [1] => Array
        (
            [0] => "
            [1] => 
            [2] => 
            [3] => '
            [4] => 
            [5] => 
            [6] => 
            [7] => 
            [8] => 
            [9] => 
            [10] => 
            [11] => 
            [12] => 
            [13] => 
            [14] => 
            [15] => 
            [16] => 
            [17] => 
            [18] => 
            [19] => 
            [20] => 
            [21] => 
            [22] => 
            [23] => 
            [24] => 
            [25] => 
            [26] => 
            [27] => 
            [28] => 
            [29] => 
            [30] => 
            [31] => "
            [32] => 
            [33] => 
            [34] => 
        )

)
于 2013-06-01T13:02:48.077 回答
1

这个想法是从特殊情况到一般情况。试试这个表达式:

Java string:
"([\"'])(?:[^\"']+|(?!\\1)[\"'])*\\1|\\|\\||<=|&&|[()\\[\\]{};=#]|[\\w.-]+"

Raw pattern:
(["'])(?:[^"']+|(?!\1)["'])*\1|\|\||<=|&&|[()\[\]{};=#]|[\w.-]+

这里的目标不是用假设的分隔符分割,而是逐个实体匹配。请注意,替代品的顺序定义了优先级(您不能放在=前面=>

您的新规范示例(需要导入模式和匹配器):

String s = "(brown) \"fox\" 'c';foo bar || 55.555;\"foo\";'bar')(;{ gray fox=-56565.4546; foo boo=\"hello\"{";
Pattern p = Pattern.compile("([\"'])(?:[^\"']+|(?!\\1)[\"'])*\\1|\\|\\||<=|&&|[()\\[\\]{};=#]|[\\w.-]+");
Matcher m = p.matcher(s) ;  

 while (m.find()) {
    System.out.println("item = `" + m.group() + "`");
}
于 2013-06-01T10:48:13.313 回答
0

您的问题主要是您试图对一个正则表达式做太多事情,因此无法理解该部分的交互。作为人类,我们都有这个麻烦。

您正在做的事情在编译器业务中有一个标准处理,称为“词法分析”。词法分析器生成器接受您感兴趣的每个单独标记的正则表达式,并构建一组复杂的状态,如果它们是可区分的,它们将挑选出单独的词素。每个标记单独的词法定义使它们单独编写变得容易且不混乱。词法分析器生成器使识别所有成员变得“容易”和高效。(如果你想定义一个包含特定引号的词素,很容易做到这一点)。

查看任何广泛可用的解析器生成器;它们都包括词法引擎,例如,JCup、ANTLR、JavaCC、...

于 2013-06-01T16:14:59.960 回答
0

也许使用JFLex之类的扫描仪生成器比使用正则表达式更容易实现您的目标。

即使您更喜欢手动编写代码,我认为将其结构化会更好。一种简单的解决方案是创建单独的方法,尝试从您的文本中“使用”您想要识别的不同类型的标记。每个这样的方法都可以判断它是否成功。这样你就有了几个较小的代码块,负责不同的标记,而不是仅仅一个难以理解和编写的大块代码。

于 2013-06-01T20:10:10.840 回答