OpenDSA 全教程

Chapter 3 Programming Tutorials

| 关于   «  6. 在 Eclipse 中进行调试   ::   目录   ::   8. Java 中的随机访问文件  »

7. 使用 Java 的 Scanner 类

7.1. Scanner 类

Java 有一个非常适合读取文本的类。 Scanner 类对于快速解析 String 中的内容很有用。参见 Scanner class API。 那么,我们如何正确地读取一个文件呢?方法不止一种。 本页演示了如何使用 Scanner 类读取命令文件(command file)。 在这个项目中,我们的程序必须处理四条命令:debug、search、add 和 delete。 先看看 输入文件。

  1. debug - 打印程序中树的信息

  2. search - 根据给定的坐标搜索一个区域

  3. add - 在给定的坐标处添加一个结点

  4. 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 条命令。

  1. insert {artist-name}<SEP>{song-name} - 使用各字段中提供的信息插入一首歌曲

  2. 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> 来分隔这些字段,我们大大降低了有效名称中含分隔符的可能性。

   «  6. 在 Eclipse 中进行调试   ::   目录   ::   8. Java 中的随机访问文件  »

关闭窗口