使用持久的外部程序进行Scala中的偶尔输入 /输出翻译



我正在编写一些Scala代码,这些代码需要使用外部命令行程序进行字符串翻译。外部程序需要数分钟的时间才能启动,然后在stdin上聆听数据(由newline终止),转换数据,并将转换后的数据打印为STDOUT(再次由Newline终止)。它将永远活着,直到收到Sigint为止。

为简单起见,让我们假设外部命令像这样运行:

$ convert input1 output2 input2 output2 $

convertinput1input2均由我键入;output1output2是由该程序编写的。我在末尾输入了控制-C返回外壳。

在我的Scala代码中,我想启动此外部程序,并保持其在后台运行(因为启动是昂贵的,但一旦初始化就可以继续运行),同时为此提供了三种方法我的其余程序都带有API,例如:

def initTranslation(): Unit def translate(input: String): String def stopTranslation(): Unit

initTranslation应该启动外部程序并将其在后台运行。 translate应将input参数放在外部程序的STDIN上(随后是newline),等待输出(接下来是NewLine),然后返回输出。 stopTranslation应将Sigint发送到外部程序。

我以前曾与Java和Scala外部流程管理合作,但是对Java管道没有太多经验,但是我不是100%确定如何将所有这些挂钩。特别是,我读到,当I/O管道在与此类似的情况下挂起时,对僵局有微妙的陷入困境。我敢肯定,我将需要一些Thread观看启动并观看initTranslation中的背景过程,一些管道将String发送到STDIN,然后阻止以等待translate中的STDOUT接收数据和Newline,然后进行某种形式stopTranslation中外部程序的终止。

我想用尽可能多的纯净Scala来实现这一目标,尽管我意识到这可能需要Java I/O库的某些位。我也不想使用任何第三方Scala或Java库(Java。*,Javax。*或Scala。*)

这三种方法是什么样的?

事实证明,这比我最初预期的要容易得多。我被各种帖子和建议误导了(So Off),这表明这将更加复杂。

警告此解决方案:

  • 所有的爪哇。是的,我知道我提到我宁愿使用Scala标准库,但这很简洁,我认为它值得一个答案。
  • 有限的错误处理 - 除其他外,如果外部程序爆炸并报告了STDERR的错误,我不会处理。当然,这可以在以后添加。
  • 使用var用于存储局部变量的用法。显然,var皱眉以供最佳实践的Scala使用,但是此示例说明了所需的对象状态,您可以按照自己的方式在自己的程序中构建变量。
  • 没有线程安全。如果您需要线程安全,因为多个线程可能会调用以下任何方法,请使用一些同步构造(例如翻译方法中的synchronized关键字)来保护自己。

解决方案:

import java.io.BufferedReader
import java.io.InputStreamReader
import java.lang.Process
import java.lang.ProcessBuilder
var process: Process = _
var outputReader: BufferedReader = _
def initTranslation(): Unit = {
  process = new ProcessBuilder("convert").start()
  outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()))
}
def translate(input: String): String = {
  // write path to external program
  process.getOutputStream.write(cryptoPath.getBytes)
  process.getOutputStream.write(System.lineSeparator.getBytes)
  process.getOutputStream.flush()
  // wait for input from program
  outputReader.readLine()
}
def stopTranslation(): Unit = {
  process.destroy()
}

最新更新