带有 Weka 命令行的 ProcessBuilder/Runtime.exec() 演示特殊行为

Posted

技术标签:

【中文标题】带有 Weka 命令行的 ProcessBuilder/Runtime.exec() 演示特殊行为【英文标题】:ProcessBuilder/Runtime.exec() with Weka Command Line Demonstrating Peculiar Behavior 【发布时间】:2019-01-17 00:05:59 【问题描述】:

下面基本上是我的全部问题的 MCVE,这要麻烦得多。您需要知道的是,直接放入终端时会运行以下行:

java -classpath /path/to/weka.jar weka.filters.MultiFilter \
    -F "weka.filters.unsupervised.attribute.ClusterMembership -I first" \
    -i /path/to/in.arff

这相对简单。基本上,我所做的只是尝试使用ClusterMembership 过滤器的所有默认设置对来自in.arff 的数据进行聚类,但我想忽略第一个属性。我在那里有 MultiFilter,因为在我的实际项目中,还有其他过滤器,所以我需要保留它。就像前面提到的,这很好用。但是,当我尝试使用 ProcessBuilder 运行同一行时,出现“引号解析错误”,并且嵌套引号的整个结构似乎都崩溃了。证明这一点的一种方法是尝试使以下内容起作用:

List<String> args = new ArrayList<String>();
args.add("java");
args.add("-cp"); 
args.add("/path/to/weka.jar");
args.add("weka.filters.MultiFilter");
args.add("-F");
args.add("\"weka.filters.unsupervised.attribute.ClusterMembership"); 
args.add("-I"); 
args.add("first\"");
args.add("-i"); 
args.add("/path/to/in.arff");
ProcessBuilder pb = new ProcessBuiler(args);

// ... Run the process below

乍一看,您可能会认为这与上面的行相同(这肯定是我天真的自己的想法)。事实上,如果我只是打印 args 并在每个字符串之间添加空格,那么如果直接复制并粘贴到终端,生成的字符串是相同的并且可以完美运行。但是,无论出于何种原因,当我收到消息(来自 Weka)Quote parse error 时,该程序将无法运行。我尝试使用谷歌搜索,发现this 的问题是关于 ProcessBuilder 如何在命令行中添加额外的引号(这导致我尝试了多种转义序列组合,所有这些都不起作用),并阅读了this 关于如何处理 ProcessBuilder/Runtime 的文章.exec() 工作(我尝试了 ProcessBuilder 和 Runtime.exec(),最终同样的问题仍然存在),但找不到与我需要的相关的任何内容。 Weka 已经有糟糕的文档,然后他们的 Wikispace 页面在几周前由于 Wikispaces 关闭而关闭,所以我在 Weka 方面发现的信息很少。

然后我的问题是:有没有办法让我在上面放置的第二个示例运行,以便我可以将参数组合在一起以获得更大的命令?我知道它可能需要一些时髦的转义序列(或者可能不需要?),或者可能是我没有考虑过的其他东西。非常感谢这里的任何帮助。

编辑:我更新了问题,希望能更深入地了解我的问题。

【问题讨论】:

【参考方案1】:

您不需要将参数组合在一起。正如您已经指出的那样,它甚至不起作用。看看当我这样调用我的 Java 程序时会发生什么:

java -jar Test.jar -i -s "-t 500"

这是我的“程序”:

public class Test 
  public static void main(String[] args) 
    for( String arg : args ) 
      System.out.println(arg);
          
  

这是输出:

-i
-s
-t 500

引号不包含在参数中,它们用于分组参数。因此,当您像以前一样将参数传递给 ProcessBuilder 时,本质上就像您在命令行上用引号将它们写成一样,它们被视为单个参数,这会使解析器感到困惑。

仅当您有嵌套组件时才需要引号,例如FilteredClassifier。也许my answer on another Weka question 可以帮助您处理这些嵌套组件。 (我最近将指向他们 wiki 的链接更改为指向 Google 缓存,直到他们建立新的 wiki。)

由于您没有具体说明是什么情况导致您考虑分组,您可以尝试为 Weka 获取一个有效的命令行,然后将其用作像我这样的程序的输入。然后你可以看到你需要如何将它们传递给ProcessBuilder

对于您的示例,我猜以下方法会起作用:

List<String> args = new ArrayList<String>();
args.add("java");
args.add("-cp"); 
args.add("/path/to/weka.jar");
args.add("weka.filters.MultiFilter");
args.add("-F");
args.add("weka.filters.unsupervised.attribute.ClusterMembership -I first");
args.add("-i"); 
args.add("/path/to/in.arff");
ProcessBuilder pb = new ProcessBuiler(args);

其他细节

Weka内部发生的情况基本上是这样的:参数中的选项首先由weka.filters.Filter处理,然后所有非通用过滤器选项由weka.filters.MultiFilter处理,其中setOptions(...)中包含以下代码:

filters = new Vector<Filter>();
while ((tmpStr = Utils.getOption("F", options)).length() != 0) 
    options2 = Utils.splitOptions(tmpStr);
    filter = options2[0];
    options2[0] = "";
    filters.add((Filter) Utils.forName(Filter.class, filter, options2));

这里,tmpStr-F 选项的值,将由Utils.splitOption(tmpStr) (source code) 处理。在那里,所有引用和取消引用的魔法都发生了,因此下一个组件将收到一个选项数组,看起来就像它是一级组件时的样子。

【讨论】:

所以实际上,我已经将其用作参考指南已有一段时间了(非常感谢顺便说一句,它真的很有帮助)。但是,我想做的是运行一个具有两个过滤器的 MultiFilter。问题是在每个过滤器中,我需要输入参数。更具体地说,我想在 ClusterMembership 过滤器中指定集群器​​,这将是嵌套的三个“层”(MultiFilter -> ClusterMembershipFilter -> Clusterer)。到目前为止,我有一个在终端中运行时可以完美运行的命令,但不能在 ProcessBuilder 中运行。 此外,我更新了我的问题,希望能让您更好地了解我遇到麻烦的地方。 我认为您需要将weka.filters.unsupervised.attribute.ClusterMembership -I first 作为不带引号的参数传递。我会更新我的答案 好吧,我现在觉得自己很蠢。在我做了所有的试验之后,我一定没有试过这个。好吧,生活和学习。再次感谢。 没问题,我们都去过那里。我将在我刚刚找到的答案中添加一些其他信息。

以上是关于带有 Weka 命令行的 ProcessBuilder/Runtime.exec() 演示特殊行为的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的 Java 代码中使用带有 Weka 的 LibSVM?

带有 IKVM 的 C# 中 Weka 的 LIbSVM

在我的 JAVA 代码中使用带有 weka 的 EM 聚类?

尝试使用带有 Weka 的 Java 对新实例进行分类时出错 - 未定义输出实例格式

带有命令行的 dotCover 过滤器

为啥 WEKA 不从命令行运行?