7. 使用 Java 的 Scanner 类¶
7.1. Scanner 类¶
Java 有一个非常适合读取文本的类。 Scanner 类对于快速解析 String 中的内容很有用。参见 Scanner class API。 那么,我们如何正确地读取一个文件呢?方法不止一种。 本页演示了如何使用 Scanner 类读取命令文件(command file)。 在这个项目中,我们的程序必须处理四条命令:debug、search、add 和 delete。 先看看 输入文件。
debug - 打印程序中树的信息
search - 根据给定的坐标搜索一个区域
add - 在给定的坐标处添加一个结点
delete - 在指定位置删除一个结点
*除 debug 之外,每条命令都带有附加参数*
思考下面这段代码示例。
import java.io.File;
import java.util.Scanner;
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
String filename = args[0];//Pass the function a full filepath
beginParsing(filename);//call the parsing function
}
public static void beginParsing(String filename) {
try {
Scanner sc = new Scanner(new File(filename));//Create our new scanner
while(sc.hasNext()) {//While the scanner has information to read
String cmd = sc.next();//Read the next term
double x; double y; double radius;
switch(cmd) {
case "debug" :
System.out.println("debug cmd activated");
break;
case "add" ://Found an add command
x = sc.nextDouble();
y = sc.nextDouble();
String name = sc.next();
System.out.println("Insert node at "+x+" "+y+" with name "+name);
break;
case "delete" ://Found a delete command
x = sc.nextDouble();
y = sc.nextDouble();
System.out.println("Remove node at "+x+" "+y);
break;
case "search" ://Found a search command
x = sc.nextDouble();
y = sc.nextDouble();
radius = sc.nextDouble();
System.out.println("Search for node near "+x+" "+y+" within radius of "+radius);
break;
default ://Found an unrecognized command
System.out.println("Unrecognized input "+cmd);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这段代码会解析命令文件,读入每条命令以及它们的参数(如果命令带参数的话)。但要注意,这段代码 并不一定安全。它假定给出的命令文件的格式是正确的,因此如果用户向程序提供了一个格式错误的文件, 程序可能会以意想不到的方式运行。
根据你文件的结构,你可能不想简单采用 token(分词)的方法。另一种做法是先读入整行,然后从那里 着手。 参考 这个输入文件。 现在我们有了必须支持的 3 条命令。
insert {artist-name}<SEP>{song-name} - 使用各字段中提供的信息插入一首歌曲
remove {artist|song} {name} - 根据给出的歌曲名或歌手名删除一首歌曲
3. print {artist|song|blocks} - 根据参数值的不同,你要么打印出数据库中所含歌手的完整列表, 要么打印歌曲,要么打印内存管理器的空闲链表(free block list)
所以这一次需要支持的命令更少了,但每条命令的选项更多了,别担心! 我们只需要稍微改一下代码。 我们看到这一次 insert 命令的 artist/song 标记之间没有任何空格,而是使用了分隔符 <SEP>。
public static void beginParsingByLine(String filename) {
try {
Scanner sc = new Scanner(new File(filename));
Scanner scancmd;//Declare two scanners one to read the file and one to read the text pulled from the file
while(sc.hasNextLine()) {//While we have text to read
String line = sc.nextLine();//Get our next line
scancmd = new Scanner(line);//Create a scanner from this line
String cmd = scancmd.next();//Get the first word (the command) on each line
String type;
switch(cmd) {
case "insert"://In the case of insert change our delimiter from white space to <SEP>
scancmd.useDelimiter("<SEP>");
String artist = scancmd.next();//Get the artist since it is before <SEP>
String song = scancmd.next();//Get the song title that follows <SEP>
System.out.println("Insert "+artist+" \\ "+song);
break;
case "remove":
type = scancmd.next();//Get the mode of deletion artist/song
String token = scancmd.nextLine();
//Since both artist titles and song titles have spaces
//get the rest of the line for the song/artist name
switch(type) {
case "artist":
System.out.println("Artist Delete: "+token);
break;
case "song":
System.out.println("Song Delete: "+token);
break;
default ://Error bad token
System.out.println("Error bad remove type " + type);
break;
}
break;
case "print"://Print command
type = scancmd.next();//get the type of print command
switch(type) {
case "artist":
System.out.println("Print artist mode");
break;
case "song":
System.out.println("Print song mode");
break;
case "blocks":
System.out.println("Print block mode");
break;
default:
System.out.println("Error bad print type" + type);
break;
}
break;
default :
System.out.println("Unrecognized input");
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
在同一行中分隔歌手名和歌曲名可能相当困难,因为任一个名字都可能包含空格或其他传统的分隔符。 通过使用 <SEP> 来分隔这些字段,我们大大降低了有效名称中含分隔符的可能性。
