重定向标准输入/输出 和 Java 虚拟机读写其他进程的数据

重定向标准输入/输出

Java 的标准输入/输出分别通过 System.in 和 System.out 来代表,在默认情况下它们分别代表键盘和显示器,当程序通过 System.in 来获取输入时,实际上是从键盘读取输入;当程序试图通过 System.out 执行输出时,程序总是输出到屏幕。

在 System 类里提供了如下三个重定向标准输入/输出的方法。

  • static void setErr(PrintStreame err):重定向“标谁”错误输出流。
  • static void setIn(InputStream in):重定向“标准”输入流。
  • static void setOut(PrintStream out):重定向“标准”输出流。

下面程序通过重定向标准输出流,将 System.out 的输出重定向到文件输出,而不是在屏幕上输出。

public class RedirectOut {
    public static void main(String[] args) {
        try (
                // 一次性创建PrintStream输出流
                PrintStream ps = new PrintStream(new FileOutputStream("out.txt"))) {
            // 将标准输出重定向到ps输出流
            System.setOut(ps);
            // 向标准输出输出一个字符串
            System.out.println("普通字符串");
            // 向标准输出输出一个对象
            System.out.println(new RedirectOut());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

上面程序中的粗体字代码创建了一个 PrintStream 输出流,并将系统的标准输出重定向到该 PrintStream 输出流。运行上面程序时将看不到任何输出——这意味着标准输出不再输出到屏幕,而是输出到 out.txt 文件,运行结束后,打开系统当前路径下的 out.txt 文件,即可看到文件里的内容,正好与程序中的输出一致。

下面程序重定向标准输入,从而可以将 System.in 重定向到指定文件,而不是键盘输入。

public class RedirectIn {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("F:\\eclipse-workspace\\demo\\src\\com\\jwen\\demo15_5\\RedirectIn.java")) {
            // 将标准输入重定向到fis输入流
            System.setIn(fis);
            // 使用System.in创建Scanner对象,用于获取标准输入
            Scanner sc = new Scanner(System.in);
            // 增加下面一行将只把回车作为分隔符
            sc.useDelimiter("\n");
            // 判断是否还有下一个输入项
            while (sc.hasNext()) {
                // 输出输入项
                System.out.println("键盘输入的内容是:" + sc.next());
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

上面程序中的粗体字代码创建了一个 FileInputStream 输入流,并使用 System 的 setIn() 方法将系统标准输入重定向到该文件输入流。运行上面程序,程序不会等待用户输入,而是直接输出了 RedirectIn.java 文件的内容,这表明程序不再使用键盘作为标准输入,而是使用 RedirectIn.java 文件作为标准输入源。

Java 虚拟机读写其他进程的数据

使用 Runtime 对象的 exec() 方法可以运行平台上的其他程序,该方法产生一个 Process 对象,Process 对象代表由该 Java 程序启动的子进程。Process 类提供了如下三个方法,用于让程序和其子进程进行通信。

  • InputStream getErrorStream():获取了进程的错误流。
  • InputStream getInputStream():获取子进程的输入流。
  • OutputStream getOutputStream():获取子进程的输出流。

注意:此处的输入流、输出流非常容易混淆,如果试图让子进程读取程序中的数据,那么应该用输入流还是输出流?不是输入流,而是输出流.要站在 Java 程序的角度来看问题,子进程读取 Java 程序的数据,就是让 Java 程序把数据输出到子进程中(就像把数据输出到文件中一样,只是现在由子进程节点代替了文件节点),所以应该使用输出流。

下面程序示范了读取其他进程的输出信息。

public class ReadFromProcess {
    public static void main(String[] args) throws IOException {
        // 运行javac命令,返回运行该命令的子进程
        Process p = Runtime.getRuntime().exec("javac");
        try (
                // 以p进程的错误流创建BufferedReader对象
                // 这个错误流对本程序是输入流,对p进程则是输出流
                BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))) {
            String buff = null;
            // 采取循环方式来读取p进程的错误输出
            while ((buff = br.readLine()) != null) {
                System.out.println(buff);
            }
        }
    }
}

上面程序中的第一行粗体字代码使用 Runtime 启动了 javac 程序,获得了运行该程序对应的子进程;第二行粗体字代码以p进程的错误输入流创建了 BufferedReader,这个输入流的流向如下图所示。

如上图所示的数据流对p进程(javac进程)而言,它是输出流:但对本程序(ReadFromProcess)而言,它是输入流——衡量输入、输出时总是站在运行本程序所在内存的角度,所以该数据流应该是输入流。运行上面程序,会看到如下图所示的运行窗口。

不仅如此,也可以通过 Process 的 getOutputStream() 方法获得向进程输入数据的流(该流对Java程序是输出流,对子进程则是输入流),如下程序实现了在 Java 程序中启动 Java 虚拟机运行另一个 Java 程序,并向另一个Java程序中输入数据。

public class WriteToProcess {
    public static void main(String[] args) throws IOException {
        // 运行java ReadStandard命令,返回运行该命令的子进程
        Process p = Runtime.getRuntime().exec("java ReadStandard");
        try (
                // 以p进程的输出流创建PrintStream对象
                // 这个输出流对本程序是输出流,对p进程则是输入流
                PrintStream ps = new PrintStream(p.getOutputStream())) {
            // 向ReadStandard程序写入内容,这些内容将被ReadStandard读取
            ps.println("普通字符串");
            ps.println(new WriteToProcess());
        }
    }
}

// 定义一个ReadStandard类,该类可以接受标准输入,
// 并将标准输入写入out.txt文件。
class ReadStandard {
    public static void main(String[] args) {
        try (
                // 使用System.in创建Scanner对象,用于获取标准输入
                Scanner sc = new Scanner(System.in);
                PrintStream ps = new PrintStream(new FileOutputStream("out.txt"))) {
            // 增加下面一行将只把回车作为分隔符
            sc.useDelimiter("\n");
            // 判断是否还有下一个输入项
            while (sc.hasNext()) {
                // 输出输入项
                ps.println("键盘输入的内容是:" + sc.next());
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
}

上面程序中的 ReadStandard 是一个使用 Scanner 获取标准输入的类,该类提供了 main() 方法,可以被运行——但此处不打算直接运行该类,而是由 WriteToProcess 类来运行 ReadStandard 类。在程序的第一行粗体字代码中,程序使用 Runtime 的 exec() 方法运行了 java ReadStandard 命令,该命令将运行 ReadStandard 类,并返回运行该程序的子进程;程序的第二行粗体字代码获得进程p的输出流——该输出流对进程p是输入流,只是对本程序是输出流,程序通过该输出流向进程 p(也就是 ReadStandard 程序)输出数据,这些数据将被 ReadStandard 类读到。

运行上面的 WriteToProcess 类,程序运行结束将看到产生了一个 out.txt 文件,该文件由 ReadStandard 类产生,该文件的内容由 WriteToProcess 类写入 ReadStandard 进程里,并由 ReadStandard 读取这些数据,并将这些数据保存到 out.txt 文件中。

猜你喜欢

转载自www.cnblogs.com/jwen1994/p/12521507.html