15、Scanner的next、nextLine方法 与 \r、\n 的一点思考和总结

来源:互联网 发布:淘宝联盟怎么设置链接 编辑:程序博客网 时间:2024/06/07 02:37

注意:所有讨论仅基于windows平台,据说linux和mac都不一样,之后装了linux再来对比试试。

首先上一段代码:

public class ScannerTest {public static void main(String[] args){System.out.println("abcd\r123");System.out.println("final");}}
在IDE中(我用的eclipse),运行结果是

abcd123final

用cmd,java命令运行,结果却不一样,结果是:

123dfinal

关于\r和\n的来历,有一个小故事,是这么说的:


 “在计算机还没有出现之前,有一种叫做电传打字机(Teletype Model 33)的玩意,每秒钟可以打10个字符。但是它有一个问题,就是打

完一行换行的时候,要用去0.2秒,正好可以打两个字符。要是在这0.2秒里面,又有新的字符传过来,那么这个字符将丢失。 于是,研制人员

想了个办法解决这个问题,就是在每行后面加两个表示结束的字符。一个叫做“回车”,告诉打字机把打印头定位在左边界;另一个叫做“换行”,

告诉打字机把纸向下移一行。 ”


\r就是回车,\n就是换行。很明显,控制台和eclipse对于“\r”的理解是不同的。

实际上当按下回车键时,传递给系统的是"\r\n"。之前关于Scanner的读入,我一直有一些疑问,结合这一点,算是弄清楚了,看以下代码:

<span style="font-family:SimSun;">import java.util.*;public class ScannerTest {public static void main(String[] args){Scanner sc = new Scanner(System.in);//sc.useDelimiter("\n");String s1 = sc.next();String s2 = sc.next();System.out.println(s1 + "; length : " + s1.length());System.out.println(s2 + "; length : " + s2.length());sc.close();}}</span>
输入“1”,按一下回车,再输入“2”,按回车,结果是:

121; length : 12; length : 1
很科学,可是Scanner的next()方法有个问题,就是会以空格,制表符等等来分隔字符串,比如调用一个next(),阻塞等待输入,输入“abc def”,则只会返回“abc”,下一次用next()会立即返回“def”。《疯狂java讲义》这个书里说,可以用useDelimiter("\n")来设定只以换行符来分隔,显然李刚就忘了还有"\r"这个东西,把上面代码的注释去掉:

import java.util.*;public class ScannerTest {public static void main(String[] args){Scanner sc = new Scanner(System.in);sc.useDelimiter("\n");String s1 = sc.next();String s2 = sc.next();System.out.println(s1 + "; length : " + s1.length());System.out.println(s2 + "; length : " + s2.length());sc.close();}}
还是依旧输入“1”,按一下回车,再输入“2”,按回车,在eclipse中结果是:

121; length : 22; length : 2
原因很简单,只以"\n"做分隔,那么next()返回的其实是“1\r”和“2\r”,而eclipse对"\r"的处理是换行加回车,即等同于“\r\n”的,所以在输出分号之前换了行,而且字符串长度也变成了2。如果改成sc.useDelimiter("\r\n");自然就一切正常了。

同时也可以预想上面那段代码在命令行中的结果,返回的"1\r"输出1之后,光标回到行首,再输出“;length ……”就把“1”给覆盖了。


再说说next()和nextLine(),nextLine()的行为有点不一样,把上面的代码改一下:

import java.util.*;public class ScannerTest {public static void main(String[] args){Scanner sc = new Scanner(System.in);sc.useDelimiter("\n");String s1 = sc.next();String s2 = sc.nextLine();System.out.println(s1 + "; length : " + s1.length());System.out.println(s2 + "; length : " + s2.length());sc.close();}}
还是输入“1”,回车,“2”,回车。

但是输入“1”,回车之后,程序直接就全部输出了,结果是

1//这是输入1; length : 2; length : 0
看了一下Scanner的文档,对于next()是这样说的

public String next()
Finds and returns the next complete token from this scanner. A complete token is preceded and followed by input that matches thedelimiter pattern. This method may block while waiting for input to scan, even if a previous invocation of hasNext() returned true.

对于nextLine()是这样描述的:

Advances this scanner past the current line and returns the input that was skipped. This method returns the rest of the current line, excluding any line separator at the end. The position is set to the beginning of the next line.

Since this method continues to search through the input looking for a line separator, it may buffer all of the input searching for the line to skip if no line separators are present.

显然,对于nextLine()完全没有提delimiter pattern以说nextLine()是不受useDelimiter方法约束的,nextLine直接跳啊跳啊,去找换行符。当输入“1”,按下回车时,其实输入给Scanner的是“1\r\n”,next方法对比着分隔符“\n",把“1\r”返回给了s1。此时定位还在\n处,即定位还没有换行,再调用nextLine,直接就发现一个“\n”于是把跳过的部分(空的)赋给了s2。

在命令行中运行,除了\r没有换行,“1”被覆盖之外,nextLine找到"\n"时也返回了。这也说明,nextLine()用\n来区分一行,但是它不仅仅以\n来区分。将分隔符的设置改一下:

import java.util.*;public class ScannerTest {public static void main(String[] args){Scanner sc = new Scanner(System.in);sc.useDelimiter("\r\n");String s1 = sc.next();String s2 = sc.nextLine();System.out.println(s1 + "; length : " + s1.length());System.out.println(s2 + "; length : " + s2.length());sc.close();}}
eclipse和命令行中运行的结果都是:

11; length : 1; length : 0
假设nextLine只以“\n”来区分行,则“\r”会被赋给s2,但是s2长度为0,什么都没有,之前已经证明\r是要占长度的。所以说nextLine也是以“\r\n”来区分一行的。作为windows下正统的换行,也是合理的,猜想nextLine是找不到\r\n时再找\n,不过感觉好像也没什么区别。


再说说,nextInt(),nextDouble()这一类的方法,看下面代码:

import java.util.*;public class ScannerTest {public static void main(String[] args){Scanner sc = new Scanner(System.in);sc.useDelimiter("\n");String s1 = sc.next();Integer s2 = sc.nextInt();System.out.println(s1 + "; length : " + s1.length());System.out.println(s2 );sc.close();}}
这个代码,执行时会出现java.util.InputMismatchException异常。原因很简单,分隔符是"\n"假设给nextInt输入“1”,回车,那么输入的就是“1\r\n”,去掉分隔符就是“1\r”,无法转换成Integer。把分隔符设置成“\r\n”就没问题了。


总之,李刚这个书,教人把分隔符设置成“\n”显然是不科学的,最近一直在看这本书,有点怀疑这书的水平了。希望他在讲I/O那章的时候能把这个东西说清楚。






0 0
原创粉丝点击