3.8 输入输出
3.8.1读取输入
(1)java.util.Scanner类与标准输入流System.in
Scanner类是一个可以使用正则表达式来解析基本类型和字符串的简单文本扫描器。
使用Scanner类的基本流程:
² Scanner类定义在java.util包中,因此在程序顶部要添加:
import java.util.*;
² 然后需要构造一个Scanner对象,并与标准输入流System.in关联:
Scanner in = new Scanner(System.in);
也可与文件流关联,例如Scanner sc = new Scanner(new File("myNumbers"));
² 现在可以使用Scanner类的各种方法实现输入操作,例如:
System.out.print(“What is your input?->”);
String ainput = in.nextLine(); //输入一行
String firststr = in.next(); //读取一个单词(以空白符作为分隔符)
int num = in.nextInt();//读取一个整数
double adouble = in.nextDouble();//读取一个浮点数
² Scanner类常用API:
Scanner(InputStream in) 构造方法:用给定的输入流(System.in|File|String等)创建一个Scanner对象。 |
String nextLine() 读取输入的下一个内容。 |
String next() 读取输入的下一个单词(以空格作为分隔符) |
int nextInt() double nextDouble() 读取并转换下一个表示整数或浮点数的字符序列 |
boolean hasNext() boolean hasNextInt() boolean hasNextLong() boolean hasNextDouble() 检测输入中是否还有其他单词(整数、长整型、浮点型) |
void close() 关闭此扫描器 |
例1:模仿用户名密码输入shell
import java.util.Scanner; public class SecondSample { public static void main(String[] args) { Scanner sin=new Scanner(System.in);
System.out.print("username:"); String username=sin.nextLine();
System.out.print("password:"); String passwd=sin.nextLine();
if(!username.equals("admin") || !passwd.equals("admin")) { System.out.println("username or passwd has error!"); System.exit(-1); } System.out.print("please input your name:"); String name=sin.nextLine();
System.out.print("please input your age:"); int age=sin.nextInt(); sin.close(); System.out.println("/nYour name is "+name+";Your age is "+age); System.out.println("Welcome!"); } } |
例2 从磁盘文件中获取输入
import java.util.Scanner; import java.io.File; public class SecondSample { public static void main(String[] args) { Scanner sin=null; try { sin=new Scanner(new File("test.txt")); } catch (Exception e) { System.out.println("要读取的文件不存在或输入流异常"); System.exit(-1); } String buf=null; while(sin.hasNextLine()) { buf=sin.nextLine(); System.out.println(buf); } sin.close(); } } |
² Scanner的分隔符模式和正则表达式
Scanner 使用分隔符模式将其输入分解为标记,默认情况下该分隔符模式与空白匹配。然后可以使用不同的 next 方法将得到的标记转换为不同类型的值。
扫描器还可以使用不同于空白的分隔符。下面是从一个字符串读取若干项的例子:
String input = "1 fish 2 fish red fish blue fish";
Scanner s = new Scanner(input).useDelimiter("//s*fish//s*");
System.out.println(s.nextInt());
System.out.println(s.nextInt());
System.out.println(s.next());
System.out.println(s.next());
s.close();
输出为:
1
2
red
blue
以下代码使用正则表达式同时解析所有的 4 个标记,并可以产生与上例相同的输出结果:
String input = "1 fish 2 fish red fish blue fish";
Scanner s = new Scanner(input);
s.findInLine("(//d+) fish (//d+) fish (//w+) fish (//w+)");
MatchResult result = s.match();
for (int i=1; i<=result.groupCount(); i++)
System.out.println(result.group(i));
s.close();
正则表达式的元字符
元字符 | 描述 |
.点 | 匹配任何单个字符。例如正则表达式r.t匹配这些字符串:rat、rut、r t,但是不匹配root。 |
$ | 匹配行结束符。例如正则表达式weasel$ 能够匹配字符串"He's a weasel"的末尾 |
^ | 匹配一行的开始。例如正则表达式^When in能够匹配字符串"When in the course of human events"的开始,但是不能匹配"What and When in the" |
* | 匹配0或多个正好在它之前的那个字符。例如正则表达式.*意味着能够匹配任意数量的任何字符。 |
/ | 这是引用符,用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式/$被用来匹配美元符号,而不是行尾,类似的,正则表达式/.用来匹配点字符,而不是任何字符的通配符。 |
[ ] | 匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配rat、rot和rut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,例如正则表达式[0-9]可以匹配任何数字字符;还可以制定多个区间,例如正则表达式[A-Za-z]可以匹配任何大小写字母。另一个重要的用法是“排除”,要想匹配除了指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符,例如正则表达式[^269A-Z] 将匹配除了2、6、9和所有大写字母之外的任何字符。 |
/< /> | 匹配词(word)的开始(/<)和结束(/>)。例如正则表达式/<the/>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。 |
/( /) | 将 /( 和 /) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 /1 到/9 的符号来引用。 |
| | 将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。 |
+ | 匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。 |
? | 匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。 |
{i} | 匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]{3} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。而正则表达式[0-9]{4,6} 匹配连续的任意4个、5个或者6个数字字符。注意:这个元字符不是所有的软件都支持的。 |
(2)java.io.Console类实现控制台输入
从上面这个模仿用户名密码输入shell的例子可以看出:因为输入是可见的,所以Scanner类不适用于从控制台读取密码。为了实现这个目的,JDK6引入了Console类(java.io.Console)来实现这个目的,例如:
Console cons=System.console();
String username = cons.readLine(“Username:”);
char[] passwd = cons.readPassword(“Password:”);//不会回显输入字符
密码采用字符数组而不是字符串,主要考虑安全性。在对密码进行处理之后,应该马上用一个填充值覆盖数组元素。
采用Console对象处理输入不如采用Scanner方便,每次只能读取一行输入,而没有能够读取一个单词或一个数值的方法。
² Console类常用API
static Console console() ——java.lang.System jdk1.0 如果有可能进行交互操作,就通过控制台窗口为交互的用户返回一个Console对象,否则返回null。对于任何一个通过控制台窗口启动的程序,都可以使用Console对象。否则,其可用性将与所使用的系统有关。例如Console cons=System.console(); |
static char[ ] readPassword(String prompt,Object…args)——java.io.Console jdk6 static String readLine (String prompt,Object…args) 显示字符串pormpt并且读取用户输入,直到输入行结束。args参数可以用来提供输入格式。 |
3.8.2 标准输出
与标准输入System.in需要借助Scanner类不同,System.out可以直接将内容输出到控制台上。
最常用:System.out.println();System.out.print();System.out.printf();
System.out.println()输出后自动换行;
System.out.print()输出后不自动换行;
System.out.printf()可用于格式化输出;
—格式化输出
Java SE5.0的System.out.printf沿用了C语言的printf方法所采用的格式化输出。
(1)控制输出的字符宽度和小数点后精度
例如:System.out.printf(“%8.2f”,x);
用8个字符的宽度和小数点后两个字符的精度打印x
(2)用于printf格式化的转换符
转换符 | 类型 | 举例 | 转换符 | 类型 | 举例 |
d | 十进制整数 | 159 | s | 字符串 | Hello |
x | 十六进制整数 | 8f | c | 字符 | H |
o | 八进制整数 | 237 | b | 布尔 | True |
f | 定点浮点数 | 1.5 | h | 散列码 | 42628b2 |
e | 指数浮点数 | 1.5e+02 | tx | 日期时间 |
|
g | 通用浮点数 | - | % | 百分号 | % |
a | 十六进制浮点数 | 0x1.fccdp4 | n | 与平台有关的行分隔符 |
例如:
语句 | 输出 | 描述 |
int xInt=128; | ||
System.out.print("The value of xInt :"); System.out.println(xInt); | The value of xInt :128 |
|
System.out.printf("%d/n",xInt); System.out.printf("%x/n",xInt); System.out.printf("%o/n",xInt); | 128 80 200 | 十进制 十六进制 八进制 |
float yFloat=23.343f; | ||
System.out.printf("%f/n",yFloat); System.out.printf("%e/n",yFloat); System.out.printf("%g/n",yFloat); System.out.printf("%a/n",yFloat); System.out.printf("%.2f%%/n",yFloat); | 23.343000 2.334300e+01 23.3430 0x1.757ceep4 23.34% | 定点浮点数 指数浮点数 通用浮点数 十六进制浮点数 百分号 |
String str="Hello"; char ch='H'; | ||
System.out.printf("%s/n",str); System.out.printf("%c/n",ch); | Hello H | 字符串 字符 |
也可以利用多个转换符控制输出,例如:
System.out.printf(“Hello,%s. Next year,you’ll be %d/n”,name,age);
(3)格式化输出的控制标志
标志 | 目的 | 举例 |
+ | 打印正数和负数的符号 | +3333.33 |
空格 | 在正数之前添加空格 | | 3333.33| |
0 | 数字前面补0 | 003333.33 |
- | 左对齐 | |3333.33 | |
( | 将负数括在括号内 | (3333.33) |
, | 添加分组分隔符 | 3,333.33 |
#(对于f格式) | 包含小数点 | 3,333 |
#(对于x或0格式) | 添加前缀0x或0 | 0xcafe |
$ | 给定被格式化的参数索引,例如%1$d,%1$x将以十进制和十六进制格式打印第1个参数 | 159 9F |
< | 格式化前面说明的数值,例如%d%<x和%1$d,%1$x同一效果 | 159 9F |
例1:用控制标志格式化整型输出
语句 | 输出 | 描述 |
int xInt= 1281; | ||
System.out.printf("%d/n",xInt); System.out.printf("%+d/n",xInt); | 1281 +1281 | 无控制标志 打印正负号 |
System.out.printf("% 8dend./n",xInt); System.out.printf("%08dend./n",xInt); System.out.printf("%-8dend./n",xInt); | 1281end. 00001281end. 1281 end. | 在正数之前添加空格 数字前面补零 左对齐 |
System.out.printf("%(d/n",xInt); System.out.printf("%,d/n",xInt); System.out.printf("%#x/n",xInt); System.out.printf("%#o/n",xInt); | 1281 1,281 0x501 02401 | 将负数括在括号内 逗号分隔 添加十六进制前缀0x 添加八进制前缀0 |
System.out.printf("%1$d %1$#x %1$#o/n",xInt); System.out.printf("%d %<#x %<#o",xInt); | 1281 0x501 02401 1281 0x501 02401 | 指定格式化参数索引 格式化前一个数值 |
int xInt = —1281; | ||
System.out.printf("%d/n",xInt); System.out.printf("%+d/n",xInt); | -1281 -1281 | 无控制标志 打印正负号 |
System.out.printf("% 8dend./n",xInt); System.out.printf("%08dend./n",xInt); System.out.printf("%-8dend./n",xInt); | -1281end. -0001281end. -1281 end. | 在正数之前添加空格 数字前面补零 左对齐 |
System.out.printf("%(d/n",xInt); System.out.printf("%,d/n",xInt); System.out.printf("%#x/n",xInt); System.out.printf("%#o/n",xInt); | (1281) -1,281 0xfffffaff 037777775377 | 将负数括在括号内 逗号分隔 添加十六进制前缀0x 添加八进制前缀0 |
System.out.printf("%1$d %1$#x %1$#o/n",xInt); System.out.printf("%d %<#x %<#o",xInt); | -1281 0xfffffaff 037777775377 -1281 0xfffffaff 037777775377 | 指定格式化参数索引 格式化前一个数值 |
例2:用控制标志格式化浮点输出
语 句 | 输 出 |
float xFloat= 3333.33f; float yFloat= - 3333.33f; | |
System.out.printf("%f , %f/n",xFloat,yFloat); | 3333.330078,-3333.330078 |
System.out.printf("%+f , %+f/n",xFloat,yFloat); System.out.printf("%(f , %(f/n",xFloat,yFloat); System.out.printf("%#f/n",yFloat); | +3333.330078,-3333.330078 3333.330078,(3333.330078) -3333.330078 |
System.out.printf("%1$f %2$#f %2$,f/n",xFloat,yFloat); | 3333.330078 -3333.330078 -3,333.330078 |
System.out.printf("%f %f %<,(.2f /n",xFloat,yFloat); | 3333.330078 -3333.330078 (3,333.33) |
注:参数索引值从1开始而不是从0开始。
(4)格式化字符串转换
可以使用s转换符格式化任意的对象。对于任意实现了Formattable接口的对象都将调用formatTo方法;否则将调用toString方法,它可以将对象转换为字符串。
例如可以使用静态的String.format方法创建一个格式化的字符串,而不打印输出:
String mesg = String.format(“Hello,%s. Next year,you’ll be %d”,name,age);
(5)printf方法中日期与时间的格式化选项
printf方法中日期与时间(java.util.Date类)的格式化以t开始,以下表中任意字母结束的两个字母格式控制格式化输出,例如System.out.printf(“%tc”,new Date() );
转换符 | 类型 | 举例 |
显示完整的日期和时间(常用) | ||
c | 完整的日期和时间 | Mon Feb 09 18:05:19 PST 2004 |
F | ISO 8601日期 | 2004-02-09 |
D | 美国格式的日期(月/日/年) | 02/09/2010 |
T | 24小时时间 | 18:04:20 |
r | 12小时时间 | 04:20:22 pm |
R | 24小时时间没有秒 | 18:10 |
年 | ||
Y | 4位数字的年(前面补0) | 2010 |
y | 年的后两位数字(前面补0) | 09 |
C | 年的前两位数字(前面补0) | 20 |
月 | ||
B | 月的完整拼写 | February |
b或h | 月的缩写 | Feb |
m | 两位数字的月(前面补0) | 02 |
日 | ||
d | 两位数字的日(前面补0) | 09 |
e | 两位数字的月(前面不补0) | 9 |
j | 三位数的年中的日子(前面补0),在001到366之间 | 068 |
星期 | ||
A | 星期几的完整拼写 | Monday |
a | 星期几的缩写 | Mon |
时间的小时 | ||
H | 两位数字的小时(前面补0),在0到23之间 | 09 |
k | 两位数字的小时(前面不补0),在0到23之间 | 9 |
I | 两位数字的小时,在0到12之间 | 11 |
时间的分钟、秒、毫秒等 | ||
M | 两位数字的分钟(前面补0) | 05 |
S | 两位数字的秒(前面补0) | 18 |
L | 三位数字的毫秒(前面补0) | 047 |
N | 九位数字的毫微秒(前面补0) | 047000000 |
时区和上下午 | ||
P | 上午或下午的大写标志 | PM |
p | 上午或下午的小写标志 | pm |
z | 从GMT起,RFC822数字位移 | +0800 |
Z | 时区 | CST |
s | 从格林威治时间1970-01-01 00:00:00起的秒数 | 1304649206 |
Q | 从格林威治时间1970-01-01 00:00:00起的毫秒数 | 1304649206278 |
例子:
转换符 | 语句 Date dt=new Date(); | 输出 | 描述 |
c F D T r R | System.out.printf("%tc/n",dt); System.out.printf("%tF/n",dt); System.out.printf("%tD/n",dt); System.out.printf("%tT/n",dt); System.out.printf("%tr/n",dt); System.out.printf("%tR/n",dt); | 星期五 五月 06 10:33:26 CST 2011 2011-05-06 05/06/11 10:33:26 10:33:26 上午 10:33 | 完整的日期和时间 ISO 8601日期 美国格式的日期(月/日/年) 24小时时间 12小时时间 24小时时间(没有秒) |
Y y C | System.out.printf("%tY/n",dt); System.out.printf("%ty/n",dt); System.out.printf("%tC/n",dt); | 2011 11 20 | 4位数字的年 年的后两位数字 年的前两位数字 |
B b m | System.out.printf("%tB/n",dt); System.out.printf("%tb/n",dt); System.out.printf("%tm/n",dt); | 五月 五月 05 | 月的完整拼写 月的缩写 两位数字的月 |
d e j | System.out.printf("%td/n",dt); System.out.printf("%te/n",dt); System.out.printf("%tj/n",dt); | 06 6 126 | 两位数字的日(前补0) 两位数字的日(不补0) 三位数的年中日子 |
A a | System.out.printf("%tA/n",dt); System.out.printf("%ta/n",dt); | 星期五 星期五 | 星期的完整拼写 星期的缩写 |
H k I | System.out.printf("%tH/n",dt); System.out.printf("%tk/n",dt); System.out.printf("%tI/n",dt); | 10 (前面补0) 10 (前面不补0) 10 | 两位数字的小时(0~23) 两位数字的小时(0~23) 两位数字的小时(0~12) |
M S L N | System.out.printf("%tM/n",dt); System.out.printf("%tS/n",dt); System.out.printf("%tL/n",dt); System.out.printf("%tN/n",dt); | 33 26 278 278000000 | 两位数字的分钟(补0) 两位数字的秒 三位数字的毫秒 九位数值的毫微秒 |
p z Z s Q | System.out.printf("%tp/n",dt); System.out.printf("%tz/n",dt); System.out.printf("%tZ/n",dt); System.out.printf("%ts/n",dt); System.out.printf("%tQ/n",dt); | 上午 +0800 CST 1304649206 1304649206278 | 上下午的小写标志 从GMT起RFC822位移 时区 从1970起的秒数 从1970起的毫秒数 |
注:许多格式化规则是本地环境特有的。例如上例中的时区是CST,星期和上下午都是中文方式输出的。又例如在德国Monday会被格式化为Montag。
另外,还可以采用一个格式化的字符串指出要被格式化的参数索引,索引必须紧跟在%后面,并以$终止。例如:
System.out.printf("%1$s %2$tB %2$te %2$tY/n" , "Date:" , new Date()); 或
System.out.printf("%s %tB %<te %<tY/n", "Date:" , new Date());
输出结果都是
Date: 五月 6 2011
总结:格式说明符的语法图
3.8.3 文件的输入与输出
(1)读取文件
n 首先import java.util.Scanner;和import java.io.File;
n 然后需要一个用java.io.File对象构造的java.util.Scanner对象,例如
Scanner in =new Scanner(new File(“myfile.txt”));或
Scanner in =new Scanner(new File(“c://mydirectory//myfile.txt”));
n 利用Scanner的方法对文件进行读取;
n 最后关闭File和Scanner对象;
(2)写入文件
n 首先import java.util.Scanner;和import java.io.File;
n 构造一个java.io.PrintWriter对象,在构造器中只需要提供文件名。例如
PrintWriter out = new PrintWriter(“myfile.txt”);
n 使用out.print(),out.printf(),out.println()等方法写入文件。
注:如果用一个不存在的文件构造一个Scanner或者用一个不能被创建的文件名构造一个PrintWriter,那么就会发生异常,因此应加入try{}catch(){}异常处理或在main方法中用throws子句标记如public static void main(String[] args)throws FileNotFoundException
² PrintWriter常用API
PrintWriter append (char c) PrintWriter append (CharSequence csq) PrintWriter append (CharSequence csq , int start , int end) 将指定的字符或字符序列添加到此writer |
PrintWriter format (Locale l , String format , Object... args) PrintWriter format(String format,object… args) 使用指定格式字符串和参数将一个格式化字符串写入此writer中 |
void print ()系列 void printf ()系列 void println ()系列 提供多种方法重载写入此writer |
void write(char[] buf) void write(char[] buf, int off, int len) void write(int c) void write(String s, int off, int len) 写入字符数组、 写入字符数组的某一部、写入单个字符、写入字符串、写入字符串的某一部分。 |
void flush() 刷新该流的缓冲。 void close() 关闭该流并释放与之关联的所有系统资源。 boolean checkError() 如果流没有关闭,则刷新流且检查其错误状态。 protected void clearError() 清除此流的错误状态。 |
(3)常用构造方法
java.util.Scanner jdk5.0
Scanner(File f) 构造一个从给定文件读取数据的Scanner |
Scanner(String data) 构造一个从给定字符串读取数据的Scanner |
java.io.PrintWriter jdk1.1
PrintWriter(File f) 构造一个将数据写入给定文件的PrintWriter |
PrintWriter(String fileName) 构造一个将数据写入文件的PrintWriter,文件名由参数指定。 |
例:将日期写入文件再从文件读出
import java.util.Scanner; import java.io.PrintWriter; import java.io.File; import java.util.Date; public class Test { public static void main(String[] args) { String dir=System.getProperty("user.dir"); System.out.println(dir); PrintWriter out; try{ out=new PrintWriter(new File("Test.txt")); }catch(Exception e) { System.out.println(e); out=null; } String tmp=String.format("%1$s %2$tB %2$te %2$tY","Today Date:",new Date()); out.println(tmp); out.format("%tc",new Date()); out.flush(); Scanner in=null; try{ in=new Scanner(new File("Test.txt")); }catch(Exception e) { System.out.println("File not find"); } String tmpin; while(in.hasNextLine()) { tmpin=in.nextLine(); System.out.println(tmpin); } } } |
注:当指定一个相对文件名时,例如“myfile.txt”,“mydirectory/myfile.txt”或“../myfile.txt”,文件位于Java虚拟机启动路径的相对位置。如果在命令行方式下用java MyProg启动程序,启动路径就是命令解释器的当前路径。然后,如果使用集成开发环境,那么启动路径将由IDE控制。因此可以使用下面的调用方式找到路径位置:
String dir=System.getProperty(“user.dir”);
如果觉得定位文件比较烦恼,则可以考虑使用绝对路径,如“c://mydirectory//myfile.txt”