Java资源网

| JAVA基础 | 环境配置 | JDBC | 线程技术 | Socket编程 | JavaMail | JAVA与XML | 设计模式 | 技术新闻 | Java认证 | 程序人生 软件下载
| JSP&Servlet | Spring | Struts | Hibernate | JBuilder | Eclipse | WebService | EJB技术 | J2ME开发 | 应用服务器 | JXTA | Ajax
Articles search文章搜索
   关键字:
   类 别:
       
New download 最新下载
· [组件]HTML Parser 1.5
· [教程]WebSphere Studio应用教程
· [组件]JDom 1.0
· [工具]Junit3.8.1
· [教程]EJB编程及J2EE系统架构和设计
· [教程]EJB教程
· [教程]J2EE Tutorial中文版
· [教程]Java编程思想2(英文)
· [教程]java编程思想(完整版)
· [教程]Java网络编程
New articles 最新文章
· 设计移动 Web 服务
· 解析XML的时候完全忽略DTD
· 理解XML Schema XML Schema 初步
· 标签库的深入研究
· 提升JSP应用程序的七大绝招
· 如何使用JDOM对XML文件进行操作
· 处理XML字符串中特殊字符
· 利用Digester把XML转换成为Java对象
· 使用WebService 和RMI远程协作
· 使用Axis开发Web Service程序
Articles top 热门文章
· Eclipse基础--plugin插件安装(6644)
· eclipse+tomcat+lomboz的安装配置说明(4774)
· Java程序员就业前景(4584)
· Windows下JAVA环境变量的设置祥解(3788)
· Tomcat下JSP、Servlet和JavaBean环境的配置(3716)
· 使用links方式安装Eclipse插件(3698)
· 一个老程序员的心理话(3533)
· linux下jdk的安装与配置(3459)
· 初学者入门:Structs中基本配置入门(3334)
· Eclipse 运行命令行参数大全(3084)
您的位置:首页>>JAVA基础>>在java中运行其他程序详解
在java中运行其他程序详解
2005-07-25   来源:javaresearch  作者:cherami
在java中运行其他程序详解

由于前段时间一个网友写信询问如何在运行其他可执行程序时将控制台的输入提交到它想运行的程序,由于考虑到他的特殊情况就帮他弄了一下,刚开始想着这个问题应该比较简单,但是在实际做的过程中才发现有很多陷阱,而且好像不能弄一个非常通用的东西出来。下面的代码是我想做出的一个通用封装器的尝试:
 
  1. import java.io.*;
  2. public class CommandWrapper {
  3.   Process process;
  4.   Thread in;
  5.   Thread out;
  6.   public CommandWrapper(Process process) {
  7.     this.process = process;
  8.     final InputStream inputStream = process.getInputStream();
  9.     //final BufferedReader r=new BufferedReader(new InputStreamReader(inputStream));
  10.     final byte[] buffer = new byte[1024];
  11.     out = new Thread() {
  12.       //String line;
  13.       int lineNumber=0;
  14.       public void run() {
  15.         try {
  16.           while (true) {
  17.             int count = inputStream.read(buffer);
  18.             System.out.println(lineNumber+":"+new String(buffer, 0, count-1));
  19.             //line=r.readLine();
  20.             //System.out.println(lineNumber+":"+line);
  21.             lineNumber++;
  22.           }
  23.         }
  24.         catch (Exception e) {
  25.         }
  26.       }
  27.     };
  28.     final BufferedReader reader =
  29.         new BufferedReader(new InputStreamReader(System.in));
  30.     final OutputStream outputStream = process.getOutputStream();
  31.     in = new Thread() {
  32.       String line;
  33.       public void run() {
  34.         try {
  35.           while (true) {
  36.             outputStream.write( (reader.readLine()+"\n").getBytes());
  37.             outputStream.flush();
  38.           }
  39.         }
  40.         catch (Exception e) {
  41.         }
  42.       }
  43.     };
  44.   }
  45.   public void startIn() {
  46.     in.start();
  47.   }
  48.   public void startOut() {
  49.     out.start();
  50.   }
  51.   public void interruptIn() {
  52.     in.interrupt();
  53.   }
  54.   public void interruptOut() {
  55.     out.interrupt();
  56.   }
  57.   public static void main(String[] args) {
  58.     try {
  59.       CommandWrapper command = new CommandWrapper(Runtime.getRuntime().exec("native2ascii"));
  60.       command.startIn();
  61.       command.startOut();
  62.     }
  63.     catch (Exception e) {
  64.       e.printStackTrace();
  65.     }
  66.   }
  67. }

我以native2ascii为范例程序和网友给我的那个程序做了对比,发现如下几个在处理这个问题时需要注意的地方:
1、由于不知道目标程序的输入输出顺序,因此只能建立两个单独的线程分别处理输入和输出,这样输入和输出就不会阻塞了。但是有些目标程序要求有特定的输入输出顺序,而经过这个类封装的结果是在任何状态下都可以输入,程序的任何输出也会被马上反映出来。这是构造通用类的第一个问题。
2、不能直接使用I/O重定向,在最开始的时候我是考虑直接使用I/O重定向的,但是实际的情况是Process的I/O的定义刚好和我的预想相反,我们从Process取得的InputStream实际上是它的输出,而取得的OutputStream是它的输入,这样就无法进行I/O重定向了,必须我们进行编码来读取程序的输出和写入控制台的输入。(这里的I/O重定向是指想将它的I/O直接重定向到系统的I/O)
3、写入控制台的输入:
            outputStream.write( (reader.readLine()+"\n").getBytes());
            outputStream.flush();
这里有两个问题值得注意:第一个是我们在控制台输入一行数据以后按下回车,那么语句reader.readLine()可以正确的得到你的输入,为什么要加那个换行符呢?这是在测试的时候发现的问题,在以native2ascii作为例子的时候发现不加这个的话它不能得到控制台的输入,但是我在替那位网友解决的问题的时候他的程序则没有这个问题,因此猜想可能是因为有的程序要求读取的一整行的数据(例如native2ascii),而大部分的命令行程序在编码的时候读取的是整数这样的值或者其他类型的值,他们是以空格或者其他的字符分隔的,因此就不需要那个额外的换行符(例如那位网友的程序读取的是一元二次方程的三个系数)。另外一个问题就是flush方法的使用,在最开始的时候没有想到要这样刷新进去,无论是否加换行符外部程序都无法读取写入的输入,后来才想到要调用一下这个方法。这个也是在我们输出的时候应该注意的一个问题,有些需要马上反应出来的输出一般都在写入以后要调用它,否则输出/输入不能马上反应出来。
4、对于程序的输出,最开始我是构造的一个BufferedReader想以行为单位输出,对于那位网友的程序,结果证明不是很好用,但是以native2ascii作为例子运行又没有问题。这个估计和外部程序的代码也有关系,如果外部程序没有输出换行符可能使用BufferedReader就会有问题。但是通过直接读取输出就没有问题了。另外需要注意的就是            System.out.println(lineNumber+":"+new String(buffer, 0, count-1));
中严格来说应该是:
         System.out.println(lineNumber+":"+new String(buffer, 0, count));
之所以减一是因为读取输入的时候人为的多加了一个换行符,如果这个地方不减一就会多输出一个空行。

基于以上的种种原因,要构造一个执行外部程序的包装器类不太好办,特别是文章中提到的几个问题。有时间和兴趣的朋友可以做一下测试,看看以上的问题和猜测是否正确。
另外附上网友的源代码,是一个fortran的程序:
             implicit none
             real a,b,c
             real d
             real root1,root2
             print*,'请输入一元二次方程的系数a,b,c:'
             read(*,*) a,b,c
             d=b**2-4.0*a*c
             if(d>=0.0) then
              root1=(-b+sqrt(d))/(2.0*a)
              root2=(-b-sqrt(d))/(2.0*a)
              print*,'root1=',root1
              print*,'root2=',root2 
             else
              print*,'一元二次方程没有实根!'
             end if
             pause
             end


在最开始给出的那个类虽然在某些应用中可能存在问题,但是对于一般的程序可能问题不是很大,当然大家可以根据上面的说明、猜测在应用在自己的项目中的时候进行一些修改和测试。如果最终有什么结论请和大家分享。
  --相关文章--
· J2EE全面介绍(二) (2007-04-13)
· 项目经验二则:读取war包中的文件及Ant使用中的OutOfMemoryError解决 (2007-04-13)
· 走向J2EE,漫长的道路 (2007-04-13)
· 步入J2EE架构和过程(2) (2007-04-13)
· 步入J2EE架构和过程(1) (2007-04-13)
· 方兴未艾的CORBA (2007-04-13)

版权所有©2005-2006 JAVA资源网 渝ICP备05007591号 虚拟主机 | 关于我们 | 联系方式 | 广告业务 | 网站地图 | 友情链接