在Scala中使用持久性外部程序进行偶尔的输入/输出转换

时间:2017-02-19 18:48:56

标签: scala

我正在编写一些需要使用外部命令行程序进行字符串转换的Scala代码。外部程序需要几分钟才能启动,然后侦听stdin上的数据(由换行符终止),转换数据,并将转换后的数据打印到stdout(再次由换行符终止)。它将永远存在,直到它收到SIGINT。

为简单起见,我们假设外部命令运行如下:

$ convert input1 output2 input2 output2 $

convertinput1input2都是我输入的; output1output2由程序写入stdout。我在最后输入了Control-C以返回shell。

在我的Scala代码中,我想启动这个外部程序,并让它在后台运行(因为启动成本很高,但一旦初始化就保持运行便宜),同时提供三种方法我的程序的其余部分使用API​​,如:

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

initTranslation应启动外部程序并使其在后台运行。 translate应该将input参数放在外部程序的stdin上(后跟换行符),等待输出(后跟换行符),然后返回输出。 stopTranslation应该将SIGINT发送到外部程序。

之前我曾使用过Java和Scala外部进程管理,但是对Java管道没有太多经验,但我不能100%确定如何将这一切联系起来。特别是,我已经读过,当I / O管道在类似的情况下被连接时,有关于死锁的细微问题。我确定我需要一些Thread来监视启动并监视initTranslation中的后台进程,有些管道将String发送到stdin,然后阻塞等待接收translate中的数据和stdout上的换行符,然后是stopTranslation中某种外部程序的终止。

我想用尽可能多的纯Scala来实现这一点,尽管我意识到这可能需要一些Java I / O库。我也不想使用任何第三方Scala或Java库(java。*,javax。*或scala。*之外的任何东西)

这三种方法会是什么样的?

1 个答案:

答案 0 :(得分:0)

事实证明,这比我的预期要容易得多。我被各种帖子和建议(关于SO)误导了,这表明这会更复杂。

对此解决方案的注意事项:

  • 所有Java。是的,我知道我提到我宁愿使用Scala标准库,但这很简洁,我认为它可以得到答案。
  • 有限的错误处理 - 除其他外,如果外部程序爆炸并向stderr报告错误,我没有处理。当然,这可以在以后添加。
  • 使用var存储局部变量。显然,var不赞成使用最佳实践Scala,但是这个示例说明了所需的对象状态,您可以根据自己的喜好在自己的程序中构建变量。
  • 没有线程安全。如果您需要线程安全性,因为多个线程可能会调用以下任何方法,请使用一些同步构造(如translate方法中的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()
}