输入与输出在Java里面相当基础,在Java各大书籍里面讲了又讲,但上面的概念往往讲得非常复杂,Java的老师强调学生必须透彻地弄得每一个类、每一个方法的意义,实际上,我们仅仅关注的是如何达到一个简单的输入输出效果。在网络上一个小小的Java输入输出包罗万象,主要是在JDK1.5推出了新型的Scanner输入,而以往的BufferedReader同样可以完成输入操作,也许多有经验的老手把自己使用惯的一套放上网络,根本不告诉别人怎么修改。下面举例子,彻底地说明白Java的输入输出,包括控制台,包括文件,其实根本就不难,完全是有迹可循。
一、基本目标
首先在C盘有个a.txt,里面存在一些内容
接着,我将写如下的一个Java小程序来说明Java的输入与输出,首先同时利用BufferReader与Scanner获取用户输入的一行东西,以说明两个方法的同理性,Scanner只不过是BufferReader的改进而已,其次利用Scanner获取用户输入的一堆东西,同时存放在一个动态数组ArrayList里面,方便以后操作,不是网上那种程序都不知道怎么操作这一堆东西。动态数组ArrayList不明白的,可以参考我之前写的《【Java】Java中的Collections类——Java中升级版的数据结构》(点击打开链接),最后把上面那个a.txt读取到Java中新型的字符串StringBuilder里面来,之后把字符串中的所有回车替换成空格,再输出到C盘的b.txt:
二、制作过程
1、首先是同时利用BufferReader与Scanner获取用户输入的一行东西,以说明两个方法的同理性,但做之前你必须在引入以下两个包:
import java.util.*;
import java.io.*;
Scanner在util里面,BufferReader在io里面,而且下面的动态数组什么的都需要这些,之后才开始工作:
public static void inputOneLine() throws IOException{
//System.in实际上与System.out互相对应,
//System.in是指用户在控制台的输入,System.out是值控制台的输出
System.out.print("请输入一行东西:");
String inputString="";
//Scanner对System.in进行读取,nextLine()读取一行,也就是读到第一个回车为止
inputString=new Scanner(System.in).nextLine();
System.out.println("你输入的是:"+inputString);
System.out.print("请输入一行东西:");
//BufferedReader的readLine()同理
inputString=new BufferedReader(new InputStreamReader(System.in)).readLine();
System.out.println("你输入的是:"+inputString);
}
(1)值得注意的是,如果扫描器Scanner还有一个next()方法,这个方法是读到第一个分隔符位置,也就是如果遇到Tab、空格、回车,Scanner就会停止读写,而缓冲区读者BufferedReader同样有一个read()方法,这个方法只能读取一个字符,读完就停止了。
(2)缓冲区读者BufferedReader必须对输入流进行操作,也就是System.in必须被转换成一个输入流才能被缓冲区读者操作,扫描器Scanner则不用,可以之前对System.in操作,所以说Scanner是进步的
(3)Scanner里面还有nextInt();等方法,它是指如果用户输入的东西是整形int,就能自动返回这个int,不用再处理,一般没有人使用这些方法,因为要增加程序的容错性、健壮性,把所有东西都认为是字符串读进来,再进行是否数的判断,再转换为一个数。
public static void inputMultiLine(){
//创建一个动态数组
ArrayList<String> inputStringArr=new ArrayList<String>();
System.out.println("请输入一堆东西,输入#结束");
Scanner scanner=new Scanner(System.in);
//这个循环不断继续,直到读进来的字符串是"#"才打断
//由于Java中(好像不止是在Java,在不少语言都是),字符串是个对象,因此,只能使用equals方法判断
//==,!=编译不出错,程序不错,但照样跑,因为对象与对象的==的意思是判断这两个是否都是String对象,不是这样的!
while(true){
String temp=scanner.nextLine();
//如果字符串不是什么都没有,不是#
if(!temp.equals("")&&!temp.equals("#")){
//那么就把它压进动态数组
inputStringArr.add(temp);
}
//如果是#则打断循环
if(temp.equals("#")){
break;
}
}
System.out.println("刚才,你输入的东西为"+inputStringArr);
//如果动态数组存在第二个函数,则把它取出并打印出来
if(inputStringArr.size()>1){
System.out.println("你输入的第二行为:"+inputStringArr.get(1));
}
}
3、最后把上面那个a.txt读取到Java中新型的字符串StringBuilder里面来,之后把字符串中的所有回车替换成空格,再输出到C盘的b.txt。
这里其实把扫描器要扫描的东西,从System.in改成一个文件就行了,这个方法由于设置到文件的输入输出,所以要跑出IO异常,至于缓冲区的读者写者在之前的《【Java】打印流与缓冲区读者完成输入与输出到文件操作》(点击打开链接)做过了,这里不做了。
public static void fileInputOutput() throws IOException{
//说明我要扫描c:\a.txt,在字符串的\必须转义成\\
Scanner scanner=new Scanner(new File("c:\\a.txt"));
StringBuilder filetoStringTemp=new StringBuilder("");
//如果还有下一行的话?hasNext()方法不会导致扫描器Scanner的游标向下跳,因此可以作为判断条件
//如果会向下跳就会出现隔行没有被读取的现象
while(scanner.hasNext()){
//StringBuilder没有重载运算符+=号,只能通过append()方法操作,同样的结果
filetoStringTemp.append(scanner.next());
//每读完一行,再补上一个换行,scanner不会把换行读进来
filetoStringTemp.append("\n");
}
//把StringBuilder转换成String,同样也可以通过其toString实现。
String filetoString=new String(filetoStringTemp);
System.out.println(filetoString);
//把所有回车换成空格,注意的是replaceAll是返回一个String,必须用来替换原来的字符串,
//它不是一个void()方法,弄完就完事了
filetoString=filetoString.replaceAll("\n", " ");
System.out.println(filetoString);
//判断这个字符串是否以"# "可以看到运行结果返回true,这方法在判断后缀名的时候很有用
System.out.println(filetoString.endsWith("# "));
//用打印流打印到文件,这里指定文件用文件写者来指定,这样就不会出现覆盖输出的问题了
PrintWriter printwriter=new PrintWriter(new FileWriter("c:\\b.txt",true));
//跟System.out.println()控制台输出方法有异曲同工之妙,只是我是输出到文件而已
//把下面这行代码倒过来看你就能理解
printwriter.println(filetoString);
//必须关闭文件打印流才能完成一次输出,否则,打印的内容永远只在内存!
printwriter.close();
System.out.println("已经把字符串filetoString输出到c盘的b.txt里面了。");
}
(1)开始先用JDK1.5之后的新型字符串StringBuilder来不停地接受从文件读过来的东西。这个东西比String好,坦诚String同样可以接受字符串,程序还更短:
String a="";
a+="";
但是这样据说会多出很多临时变量让Java回收,效率不高,遇到一些超长的字符串,最好使用JDK1.5之后的新型字符串StringBuilder,这东西效率高,此外JDK1.5还新增了StringBuffer字符串,但这东西由于里面有线程互斥保护机制,所以效率没StringBuilder高,但如果出现多线程操作同一个字符串,使用StringBuffer你就不用写一段线程互斥的代码,线程互斥是什么,之前在《【Java】线程并发、互斥与同步》(
点击打开链接)已经说过了。
(2)字符串里面还有indexof与substring的实用方法,曾经在《【Java】截取字符串中的首个图片地址》(点击打开链接)做过了,不再赘述。
三、总结与展望
于是,整个程序如下,在主函数调用上面的三个方法就能够直接执行了,这些方法必须定义为静态方法,才能被这样调用:
import java.util.*;
import java.io.*;
public class ScannerTest {
public static void inputOneLine() throws IOException {
// System.in实际上与System.out互相对应,
// System.in是指用户在控制台的输入,System.out是值控制台的输出
System.out.print("请输入一行东西:");
String inputString = "";
// Scanner对System.in进行读取,nextLine()读取一行,也就是读到第一个回车为止
inputString = new Scanner(System.in).nextLine();
System.out.println("你输入的是:" + inputString);
System.out.print("请输入一行东西:");
// BufferedReader的readLine()同理
inputString = new BufferedReader(new InputStreamReader(System.in))
.readLine();
System.out.println("你输入的是:" + inputString);
}
public static void inputMultiLine() {
// 创建一个动态数组
ArrayList<String> inputStringArr = new ArrayList<String>();
System.out.println("请输入一堆东西,输入#结束");
Scanner scanner = new Scanner(System.in);
// 这个循环不断继续,直到读进来的字符串是"#"才打断
// 由于Java中(好像不止是在Java,在不少语言都是),字符串是个对象,因此,只能使用equals方法判断
// ==,!=编译不出错,程序不错,但照样跑,因为对象与对象的==的意思是判断这两个是否都是String对象,不是这样的!
while (true) {
String temp = scanner.nextLine();
// 如果字符串不是什么都没有,不是#
if (!temp.equals("") && !temp.equals("#")) {
// 那么就把它压进动态数组
inputStringArr.add(temp);
}
// 如果是#则打断循环
if (temp.equals("#")) {
break;
}
}
System.out.println("刚才,你输入的东西为" + inputStringArr);
// 如果动态数组存在第二个函数,则把它取出并打印出来
if (inputStringArr.size() > 1) {
System.out.println("你输入的第二行为:" + inputStringArr.get(1));
}
}
public static void fileInputOutput() throws IOException {
// 说明我要扫描c:\a.txt,在字符串的\必须转义成\\
Scanner scanner = new Scanner(new File("c:\\a.txt"));
StringBuilder filetoStringTemp = new StringBuilder("");
// 如果还有下一行的话?hasNext()方法不会导致扫描器Scanner的游标向下跳,因此可以作为判断条件
// 如果会向下跳就会出现隔行没有被读取的现象
while (scanner.hasNext()) {
// StringBuilder没有重载运算符+=号,只能通过append()方法操作,同样的结果
filetoStringTemp.append(scanner.next());
// 每读完一行,再补上一个换行,scanner不会把换行读进来
filetoStringTemp.append("\n");
}
// 把StringBuilder转换成String,同样也可以通过其toString实现。
String filetoString = new String(filetoStringTemp);
System.out.println(filetoString);
// 把所有回车换成空格,注意的是replaceAll是返回一个String,必须用来替换原来的字符串,
// 它不是一个void()方法,弄完就完事了
filetoString = filetoString.replaceAll("\n", " ");
System.out.println(filetoString);
// 判断这个字符串是否以"# "可以看到运行结果返回true,这方法在判断后缀名的时候很有用
System.out.println(filetoString.endsWith("# "));
// 用打印流打印到文件,这里指定文件用文件写者来指定,这样就不会出现覆盖输出的问题了
PrintWriter printwriter = new PrintWriter(new FileWriter("c:\\b.txt",
true));
// 跟System.out.println()控制台输出方法有异曲同工之妙,只是我是输出到文件而已
// 把下面这行代码倒过来看你就能理解
printwriter.println(filetoString);
// 必须关闭文件打印流才能完成一次输出,否则,打印的内容永远只在内存!
printwriter.close();
System.out.println("已经把字符串filetoString输出到c盘的b.txt里面了。");
}
public static void main(String[] args) throws IOException {
inputOneLine();
inputMultiLine();
fileInputOutput();
}
}