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

在Scala中使用持久性外部程序进行偶尔的输入/输出转换,scala,Scala,我正在编写一些Scala代码,需要使用外部命令行程序进行字符串转换。外部程序启动需要很多分钟,然后侦听stdin上的数据(以换行符终止),转换数据,并将转换后的数据打印到stdout(再次以换行符终止)。它将永远活着,直到收到信号 为简单起见,假设外部命令的运行方式如下: $convert 输入1 输出2 输入2 输出2 $ convert、input1和input2都是我自己打字的输出1和输出2由程序写入标准输出。我在最后键入Control-C返回shell 在我的Scala代码中,我希望启

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

为简单起见,假设外部命令的运行方式如下:

$convert
输入1
输出2
输入2
输出2
$

convert
input1
input2
都是我自己打字的<代码>输出1和
输出2
由程序写入标准输出。我在最后键入Control-C返回shell

在我的Scala代码中,我希望启动这个外部程序,并让它在后台运行(因为启动成本很高,但初始化后继续运行成本很低),同时为程序的其余部分提供三种方法,如:


def initTranslation():单位
def翻译(输入:字符串):字符串
def STOPTRANSACTION():单位

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

我以前使用过Java和Scala外部流程管理,但没有太多Java管道方面的经验,但我不完全确定如何将这一切连接起来。特别是,我已经读到,当I/O管道在类似的情况下连接起来时,死锁会有一些微妙的问题。我确信我需要一些
线程
来监视启动和
initTranslation
中的后台进程,一些管道将
字符串
发送到stdin,然后阻塞以等待接收数据,并在
translation
中的stdout上有一个换行,然后在
stopTranslation
中以某种方式终止外部程序

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


这三种方法看起来是什么样的?

事实证明,这比我最初预期的要简单得多。我被各种各样的帖子和建议(offso)误导了,这些帖子和建议暗示这将更加复杂

此解决方案的注意事项:

  • 全是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()
}