0%
这是一片思考的空间 -- arthinking
Spring 重构&代码整洁之道 软件设计 JVM 并发编程 数据结构与算法 分布式 存储 网络 微服务 设计模式
Java技术栈 - 涉及Java技术体系

Java笔记 - 扫描输入

本文主要内容:

1、在这篇文章中,通过Scanner类和JavaSE1.4之前的输入方法对比,为您呈现Scanner的优势。

2、简单介绍了Scanner的常用方法和异常的处理。

3、通过useDelimiter()方法设置定界符和delimiter()返回当前使用的定界符Pattern对象。

4、通过Scanner用正则表达式进行扫描。

5、StringTokenizer的介绍与Scanner和split()方法的对比。

Scanner:

在Java SE1.4之前,从文件或标准输入读取数据是一件很痛苦的事情,一般都是读一行文本,对其进行分词,然后使用Integer,Double等类的各种解析方法来解析数据:

// 使用StringReader将String转换为可读的流对象,然后使用BufferedReader对象封装,使得可以一次读入一行文本
BufferedReader reader = new BufferedReader(new StringReader("Jason\n23 7"));
try {
System.out.println("Input your name:");
String name = reader.readLine();
System.out.println("name: " + name);
System.out.println("How old are you and what's you favorite number?");
String line = reader.readLine();
String[] numArr = line.split(" ");
// 逐个字符串转换解析
int age = Integer.parseInt(numArr[0]);
int num = Integer.parseInt(numArr[1]);
System.out.println("age: " + age);
System.out.println("favorite number: " + num);
} catch (IOException e) {
e.printStackTrace();
}

输出为:

Input your name:
name: Jason
How old are you and what's you favorite number?
age: 23
favorite number: 7

在JavaSE5中新增了Scanner类,支持扫描输入,使用起来就非常的方便了:

// 使用StringReader将String转换为可读的流对象,然后使用BufferedReader对象封装,使得可以一次读入一行文本
BufferedReader reader = new BufferedReader(new StringReader("Jason\n23 7"));
Scanner stdin = new Scanner(reader);
System.out.println("Input your name: ");
String name = stdin.nextLine();
System.out.println("name: " + name);
System.out.println("How old are you and what's your favorite number?");
int age = stdin.nextInt();
int number = stdin.nextInt();
System.out.println("age: " + age);
System.out.println("favorite numver: " + number);

输出为:

Input your name:
name: Jason
How old are you and what's your favorite number?
age: 23
favorite numver: 7

Scanner构造器可以接受任何类型的输入对象,如File,InputStream,String,或者Readable对象。所有的基本数据类型都有对应的next方法,包括BigDecimal和BigInteger,普通的next()方法返回下一个String。还有对应的hasNext方法,判断下一个输入分词是否所需的类型。

Scanner在输入结束时会抛出IOException异常,所以Scanner会把IOException吞掉,通过ioException()方法可以找到最近发生的异常,可以使用此方法在必要时进行检查。

Scanner定界符:

默认情况下,Scanner是通过空白字符对输入进行分词的,可以通过useDelimiter方法使用正则表达式指定自己所需的定界符,通过delimiter()方法返回当前正在作为定界符使用的Pattern对象:

Scanner scanner = new Scanner("1, 2, 3, 4, 5, 6, 7, 8");
// 使用delimiter()获取当前正在作为定界符的Pattern对象
System.out.println(scanner.delimiter()); // 输出: \p{javaWhitespace}+
while(scanner.hasNext()){
System.out.print("[" + scanner.next() + "]");
}
// 输出: [1,][2,][3,][4,][5,][6,][7,][8]
scanner = new Scanner("1, 2, 3, 4, 5, 6, 7, 8");
// 使用useDelimiter()方法来设定自己所需的定界符
scanner.useDelimiter("\\s*,\\s*");
while(scanner.hasNextInt()){
System.out.print("[" + scanner.nextInt() + "]");
}
// 输出: [1][2][3][4][5][6][7][8]

用正则表达式扫描:

除了扫描基本的数据类型,也可以通过Scanner的以下方法:

next

public String next(String pattern)

如果下一个标记与从指定字符串构造的模式匹配,则返回下一个标记。如果匹配操作成功,则扫描器执行与该模式匹配的输入。此方法调用 next(pattern) 的行为与调用 next(Pattern.compile(pattern)) 完全相同。

使用自定义的正则表达式进行扫描,这在扫描复杂数据的时候非常有用,注意,这里仅仅针对下一个输入分词进行匹配,不能跨越两个分词:

String content = "192.168.0.1\n" +
"192.168.0.2\n" +
"192.168.12.34";
Scanner scanner = new Scanner(content);
String pattern = "(\\d+[.]\\d+[.]\\d+[.]\\d+)";
// 下面用正则表达式进行扫描
while(scanner.hasNext(pattern)){
scanner.next(pattern);
MatchResult match = scanner.match();
String ip = match.group(1);
// 格式化输出IP
System.out.format("ip address: %s\n", ip);
}

StringTokenizer:

这个类在Java引入了正则表达式和Scanner类之后就比较少用到了,在这之前(JavaSE1.4),分割字符串的唯一方法是使用StringTokenizer来分词。而相比之下,前者使用起来更加简单方便,下面是一个使用StringTokenizer进行分词的例子:

// 使用StringTokenizer的方式
String content = "It is raining outside. 外面下着雨...";
StringTokenizer stoke = new StringTokenizer(content);
while(stoke.hasMoreElements()){
System.out.print(stoke.nextToken() + " ");
}

// 使用split的方式
System.out.println(Arrays.toString(content.split(" ")));

// 使用Scanner的方式
Scanner scanner = new Scanner(content);
while(scanner.hasNext()){
System.out.print(scanner.next() + " ");
}

使用正则表达式和Scanner对象,我们能够更加复杂的模式来分割一个字符串,而StringTOkenizer则比较困难,现在基本上都没有使用这个类了。

欢迎关注我的其它发布渠道

订阅IT宅
内功修炼
Java技术栈
Java架构杂谈是IT宅精品文章公众号,欢迎订阅:
📄 网络基础知识:两万字长文50+张趣图带你领悟网络编程的内功心法 📄 HTTP发展史:三万长文50+趣图带你领悟web编程的内功心法 📄 HTTP/1.1:可扩展,可靠性,请求应答,无状态,明文传输 📄 HTTP/1.1报文详解:Method,URI,URL,消息头,消息体,状态行 📄 HTTP常用请求头大揭秘 📄 HTTPS:网络安全攻坚战 📄 HTTP/2:网络安全传输的快车道 📄 HTTP/3:让传输效率再一次起飞 📄 高性能网络编程:图解Socket核心内幕以及五大IO模型 📄 高性能网络编程:三分钟短文快速了解信号驱动式IO 📄 高性能网络编程:彻底弄懂IO复用 - IO处理杀手锏,带您深入了解select,poll,epoll 📄 高性能网络编程:异步IO:新时代的IO处理利器 📄 高性能网络编程:网络编程范式 - 高性能服务器就这么回事 📄 高性能网络编程:性能追击 - 万字长文30+图揭秘8大主流服务器程序线程模型
📄 Java内存模型:如果有人给你撕逼Java内存模型,就把这些问题甩给他 📄 一文带你彻底理解同步和锁的本质(干货) 📄 AQS与并发包中锁的通用实现 📄 ReentrantLock介绍与使用 📄 ReentrantReadWriteLock介绍与使用 📄 ReentrantLock的Condition原理解析 📄 如何优雅的中断线程 📄 如何优雅的挂起线程 📄 图解几个好玩的并发辅助工具类 📄 图解BlockingQueue阻塞队列
📄 消息队列那么多,为什么建议深入了解下RabbitMQ? 📄 高并发异步解耦利器:RocketMQ究竟强在哪里? 📄 Kafka必知必会18问:30+图带您看透Kafka
📄 洞悉MySQL底层架构:游走在缓冲与磁盘之间 📄 SQL运行内幕:从执行原理看调优的本质 📄 洞悉Redis技术内幕:缓存,数据结构,并发,集群与算法