解析多个单词组合的字符串的最佳方法是什么?

时间:2013-05-08 06:33:56

标签: java string parsing

我正在编写一个试图从自然语言中获取意义的程序。程序将接受一个字符串,并查看它是否包含某些单词组合。有关示例,请参阅以下代码段:

if (phrase.contains("turn")) { // turn something on/off
    if (phrase.contains("on") && !phrase.contains("off")) { // turn something ON
        if (phrase.contains("pc") || phrase.contains("computer")) // turn on computer
            turnOnComputer();
        else if (phrase.contains("light") || phrase.contains("lamp")) // turn on lights
            turnOnLights();
        else
            badPhrase();
    }
    else if (phrase.contains("off") && !phrase.contains("on")) { // turn something OFF
        if (phrase.contains("pc") || phrase.contains("computer")) // turn off computer
            turnOffComputer();
        else if (phrase.contains("light") || phrase.contains("lamp")) // turn off lights
            turnOffLights();
        else
            badPhrase();
    }
    else {
        badPhrase();
    }
}
else {
    badPhrase();
}

正如您所看到的,如果我想要解释的不仅仅是几个含义,这很快就会成为无法管理的代码。我怎样才能更好地管理这个?

5 个答案:

答案 0 :(得分:3)

Apache OpenNLP 是一种基于机器学习的工具包,用于处理自然语言文本。

它包括句子检测器标记器词性(POS)标记器树库解析器

Manual for NLP

Download

希望它有所帮助; )

答案 1 :(得分:2)

关键字定位当然只能管理一小组单词和/或非常有限的输入语言。好的,也许周围的文字无关紧要。

然而,对于那种自然语言解析,您需要一种更复杂的方法,例如首先对文本进行标记,然后尝试查找单词之间的句法关系(从直接邻居开始并稍后扩展范围)。最后使用您作为控制代码找到的句法关系来推动您的行动决策。

正则表达式可能不是这里的答案,因为它们需要非常严格的输入。考虑这样一句话:

  

请勿关灯,而是将其打开。

RE和您的原始方法都不会给您任何明智的结果。另外,不要忘记语法或语法错误。

答案 2 :(得分:1)

使用正则表达式来实现你想要的,因为正则表达式可以匹配字符串的组合。

答案 3 :(得分:1)

首先,我不确定您的方法对自然语言处理的适用性。此外,还没有现有的NLP库吗?特别是在NLP中,我知道有时候顺序和词性很重要,加上这种方法对于单词变化不是很强大。

但是,如果你想坚持你的方法,一个让它更具可读性和更易维护的想法(见下面的更全面的优点/缺点)是这样的:

StringFinder finder = new StringFinder(phrase);
if        (finder.containsAll("turn", "on").andOneOf("computer", "pc").andNot("off").matches()) {
    turnOnComputer();
    return;
} else if (finder.containsAll("turn", "off").andOneOf("computer", "pc").andNot("on").matches()) {
    turnOffComputer();
    return;
} else if (finder.containsAll("turn", "on").andOneOf("light", "lamp").andNot("off").matches()) {
    ...
} else if (finder.containsAll("turn")) { // If we reached this point
    badPhrase();
} else if (...

有类似的东西:

class StringFinder {
    private final String phrase;
    private final Map<String, Boolean> cache = new HashMap<String, Boolean>();

    public StringFinder(String phrase) { this.phrase = phrase; }

    public StringFinder containsAll(String... strings) {
        for (String string : strings) {
            if (contains(string) == false) return new FailedStringFinder(phrase);
        }
        return this;
    }

    public StringFinder andOneOf(String... strings) {
        for (String string: strings) {
            if (contains(string)) return this;
        }
        return FailedStringFinder(phrase);
    }

    public StringFinder andNot(String... strings) {
        for (String string : strings) {
            if (contains(string)) return new FailedStringFinder(phrase);
        }
        return this;
    }

    public boolean matches() { return true; }

    private boolean contains(String s) {
        Boolean cached = cache.get(s);
        if (cached == null) {
            cached = phrase.contains(s);
            cached.put(s, cached);
        }
        return cached;
    }


}

class FailedStringFinder extends StringFinder {
    public boolean matches() { return false; }

    // The below are actually optional, but save on performance:
    public StringFinder containsAll(String... strings) { return this; }
    public StringFinder andOneOf(String... strings) { return this; }
    public StringFinder andNot(String... strings) { return this; }
}

缺点:

  • 重复检查:多次检查“转弯”。
  • 重复模式(但请参阅下面的优点)。

优点:

  • 相对简洁的代码。
  • 检查是重复的,但是已缓存,因此性能仍然很高。
  • 条件非常接近操作,导致代码非常易读。
  • 不嵌套条件允许在不重构代码的情况下更改特定操作所需的条件,从而导致更多可维护代码。
  • 易于更改条件和操作的顺序,以控制优先级。
  • 缺乏嵌套使得将来更容易并行化。
  • 灵活的条件检查:例如,您可以向StringFinder添加方法以匹配重复检查,例如:public StringFinder containsOnAndNotOff() { return containsAll("on").andNot("off"); },或者匹配您需要的某些异国情况,例如andAtLeast3Of(String... strings) {...}
    • 缓存还可以扩展到不仅记住单词是否出现,还要记住是否出现整个模式。
    • 您还可以添加最终条件:andMatches(Pattern p)(使用正则表达式模式) - 实际上,您可以使用正则表达式对许多其他检查进行建模。然后它可以很容易地缓存 - 而不是使用字符串作为键,使用模式。

答案 4 :(得分:0)

这是@Oak

提供的答案中的固定代码
import java.util.HashMap;
import java.util.Map;

class StringFinder {
    private final String phrase;
    private final Map<String, Boolean> cache = new HashMap<String, Boolean>();

    public StringFinder(String phrase) { this.phrase = phrase; }

    public StringFinder containsAll(String... strings) {
        for (String string : strings) {
            if (contains(string) == false) return new FailedStringFinder(phrase);
        }
        return this;
    }

    public StringFinder andOneOf(String... strings) {
        for (String string: strings) {
            if (contains(string)) return this;
        }
        return new FailedStringFinder(phrase);
    }

    public StringFinder andNot(String... strings) {
        for (String string : strings) {
            if (contains(string)) return new FailedStringFinder(phrase);
        }
        return this;
    }

    public boolean matches() { return true; }

    private boolean contains(String s) {
        Boolean cached = cache.get(s);
        if (cached == null) {
            cached = phrase.contains(s);
            cache.put(s, cached);
        }
        return cached;
    }


}

class FailedStringFinder extends StringFinder {

    public FailedStringFinder(String phrase) {
        super(phrase);
    }

    public boolean matches() { return false; }

    // The below are actually optional, but save on performance:
    public StringFinder containsAll(String... strings) { return this; }
    public StringFinder andOneOf(String... strings) { return this; }
    public StringFinder andNot(String... strings) { return this; }
}