java快速输入输出
主要记录在PTA时经常遇到的超时问题,基本都是由于Scanner耗时过长导致的,至于输出的话基本就是用
static PrintWriter out = new PrintWriter(System.out);
输出的时候有多种方法,可以用printf
,也可以用println
,看个人需要了。输出完的时候记得要加上这段,不然没办法打印。
out.flush(); //关闭输出流
后面主要介绍的都是快读的方法。
StreamTokenzier类
这个类基本可以应付大部分快读问题,而且由于经常遇到的是组合输入,所以用这种方法会比较多。这个方法也是笔者目前使用的效率最高的快读方法。
但使用这个类的时候要注意一个问题,就是它读取的时候会自动解析空格 , 字母 , 数字 , 字符串引号和注释字符 ,在读取字符串的时候需要注意这种情况,以免丢失必要的数据。除此之外,在解析数字时,其默认是以double
进行解读,所以可能会在转换成long
型的时候出现误差,需要多加注意。
初始化方法
static StreamTokenizer sc =new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
输入方法
sc.nextToken() //读取前都要加,用于当前输入流的下一个标记
//字符串
String s = sc.sval;
//int型数据
int a = (int)sc.nval;
//double型数据
double d = sc.nval;
//long型数据
long l = (long)sc.nval;
long l = Long.parseLong(sc.sval); //如果数据非常大的时候导致精度丢失,建议先读取字符串再转为long
StreamTokenizer的基本方法
由于会自动对一些字符进行解析,在读取纯数字的字符串的时候就有很多不便,为解决这个问题先介绍一些基本方法。
- commenChar(int ch) - 指定某个字符为注释字符,此字符之后直到行结尾都被stream tokenizer忽略。
- eolIsSignificant(boolean flag) -** 决定一个行结束符是否被当作一个基本的符号处理,如果是true,则被当作一个基本符号,不当作普通的分隔符,如果是false,则保持原义,即当作普通的分隔符。
- lineno() - 返回当前流所在的行号。
- lowerCaseMode(boolean flag) - 决定是否读取一个单词时是否转变成小写。
- nextToken() - 分析下一个。
- ordinaryChar(int ch) - 指定字符在这个tokenizer中保持原义,即只会把当前字符认为普通的字符,不会有其他的语义。
- ordinaryChars(int low, int hi) - 指定范围内的字符保持语义,同上
- parseNumbers() - 当stream tokenizer遭遇到一个单词为双精度的浮点数时,会把它当作一个数字,而不是一个单词。
- pushBack() - 回退,会引起下一个nextToken方法返回当前值。
- quoteChar(int ch) - 指定当前字符为当前tokenizer中的分隔符,在两个符号之间被当作一个字符串解析。
- resetSyntax() - 重置语法表使所有的字符都被认为是“ordinary”。
- slashSlashComments(boolean flag) - 如果为true,则/与/之间的都被认为是注释,反之,不是。
- slashStartComments(boolean flag) - 如果为true,则//之后到行结尾的所有都被认为是注释,反之,不是。
- whitespaceChars(int low, int hi) - 字符low与hi之间的所有字符都被当作为空格符,即被认识为tokenzier的分隔符。
- wordChars(int low, int hi) - 字符low与hi之间的所有字符都被当作为单词的要素。 一个单词是由一个单词要素后面跟着0个或者更多个单词要素或者数字要素。
读取纯数字字符串
前面提到了读纯数字字符串的时候会被识别成数字,读不出来,这个时候就需要提前将数字指定成单词了。方法如下:
sc.ordinaryChars('0','9'); //将0-9认作普通的字符
sc.wordChars('0','9'); //将0-9认作单词要素
但可能会遇到一个新的问题,如果我读取完纯数字字符串后,又想读取数字怎么办呢?(参考
PTA L1-005)
这里提供两个思路,要么把空格也当作单词要素一起读进来用split解决,另一种就是我要讲的一种方法。
//先将数字当作字符进行读取
sc.ordinaryChars('0','9');
sc.wordChars('0','9');
sc.nextToken();
String s = sc.sval;
sc.parseNumbers(); //用这个方法可以把后面的数字都正常解析
sc.nextToken();
int testId = (int)sc.nval;
sc.nextToken();
int formalId = (int)sc.nval;
来点例子
前面也讲的差不多了,有蛮多方法虽然没讲到但是看字面还是蛮好理解的,而且目前写题也没怎么用到其他方法就是了,用到了再补。这里给两道题爽一爽。虽然说L1-005那道我还是没有全部AC
PTA L1-005,除了第二个测试点都能过
import java.io.*;
import java.util.*;
public class Main
{
static StreamTokenizer sc =new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static PrintWriter out = new PrintWriter(System.out);
public static void main(String[] args) throws IOException {
sc.nextToken();
int N = (int)sc.nval;
String[] TestToId = new String[N + 1];
int[] TestToFormal = new int[N + 1];
for (int i = 0; i < N; i++) {
sc.ordinaryChars('0','9');
sc.wordChars('0','9');
sc.nextToken();
String s = sc.sval;
sc.parseNumbers();
sc.nextToken();
int testId = (int)sc.nval;
sc.nextToken();
int formalId = (int)sc.nval;
TestToId[testId] = s;
TestToFormal[testId] = formalId;
}
sc.nextToken();
int M = (int)sc.nval;
int[] testM = new int[M];
for (int i = 0; i < M; i++) {
sc.nextToken();
testM[i] = (int)sc.nval;
}
for (int id : testM) {
out.printf("%s %s\n",TestToId[id],TestToFormal[id]);
}
out.flush();
out.close();
}
}
import java.io.*;
import java.util.*;
public class Main
{
static StreamTokenizer sc =new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static PrintWriter out = new PrintWriter(System.out);
public static void main(String[] args) throws IOException
{
sc.nextToken();
int n = (int)sc.nval;
String l[] = new String[n + 10], r[] = new String[n + 10];
sc.ordinaryChars('0','9');
sc.wordChars('0','9');
sc.wordChars(':',':'); //这里是因为分号也会被当作分隔符,加进去一起解析
for (int i = 1; i <= n; i++)
{
sc.nextToken();
l[i] = sc.sval;
sc.nextToken();
String cache = sc.sval;
sc.nextToken();
r[i] = sc.sval;
}
Arrays.sort(l, 1, n + 1);
Arrays.sort(r, 1, n + 1);
if (!l[1].equals("00:00:00"))
out.println("00:00:00 - " + l[1]);
for (int i = 2; i <= n; i++)
{
if (!r[i - 1].equals(l[i]))
out.println(r[i - 1] + " - " + l[i]);
}
if (!r[n].equals("23:59:59"))
out.printf(r[n] + " - 23:59:59");
out.flush();
out.close();
}
}
BufferedReader类
如果只读整行的字符串其实这个就够用了,还省点事。但至于具体的时间对比就还没做。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //初始化
String s = br.readLine();
自定义Reader类
这里是参考http://www.solohsu.com/blog/2014/04/07/faster-java-io-in-acm/制作的快速Scanner方法,对于L2-042
的话能过四个测试点,还算ok
/** Class for buffered reading int and double values */
class Reader {
static BufferedReader reader;
static StringTokenizer tokenizer;
/** call this method to initialize reader for InputStream */
static void init(InputStream input) {
reader = new BufferedReader(
new InputStreamReader(input) );
tokenizer = new StringTokenizer("");
}
/** get next word */
static String next() throws IOException {
while ( ! tokenizer.hasMoreTokens() ) {
//TODO add check for eof if necessary
tokenizer = new StringTokenizer(
reader.readLine() );
}
return tokenizer.nextToken();
}
static int nextInt() throws IOException {
return Integer.parseInt( next() );
}
static double nextDouble() throws IOException {
return Double.parseDouble( next() );
}
}