侧边栏壁纸
博主头像
GabrielxD

列車は必ず次の駅へ。では舞台は?私たちは?

  • 累计撰写 675 篇文章
  • 累计创建 128 个标签
  • 累计收到 26 条评论

目 录CONTENT

文章目录

C++ / Java在算法竞赛中的IO处理

GabrielxD
2022-11-28 / 0 评论 / 2 点赞 / 643 阅读 / 4,257 字
温馨提示:
本文最后更新于 2024-03-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

C++

输入常用到的方法

std::cin

在标头 <iostream> 定义。

全局对象 std::cin 与标准 C 输入流 stdin 关联。

示例
// 读入整数(跳过中间的所有连续空白符)
int a, b;
cin >> a >> b;

// 读入字符串到c++风格字符串(读到空白符停止)
string s;
cin >> s;

scanf

在标头 <stdio.h> 定义。

stdin 读取数据,按照 format 转译,并将结果存储到指定位置。

用法

int scanf(format, ...);

参数

  • format:指向指定读取输入方式的空终止字符串的指针。
    格式字符串由下列内容组成:

    • 非空白多字节字符,除了 % :每个格式字符串中的这种字符处理来自输入流的准确同一字符,或若它与流的下个字符比较不相等则导致函数失败。

    • 空白符:任何格式字符串中的单个空白符处理所有来自输入的可用连续空白符(如同通过于循环中调用 isspace 确定)。注意格式字符串中 "\n"" " "\t\t" 或其他空白无区别。

    • 转换指示:每个转换指示拥有下列格式:

      • 引入用 % 字符
      • (可选) 赋值抑制字符 ***** 。若存在此选项,则此函数不将结果赋值给任何接收用参数。
      • (可选) 指定最大域宽的整数数字(大于零),即函数进行在当前转换指示所指定的转换时,允许处理的最大字符数。注意若不提供宽度,则 %s%[ 可能导致缓冲区溢出。
      • (可选) 指定接收参数大小的长度修饰符,即实际目标类型。这影响转换准确性和溢出规则。默认目标类型对每个转换类型不同(见下表)。
      • 转换格式指示符

      下列格式指示符可用:

      image-20221128012633128

  • ...:接收用参数

返回值

  • 成功赋值的接收参数的数量(可以为零,在首个接收用参数赋值前匹配失败的情况下),或者若输入在首个接收用参数赋值前发生失败,则为EOF
示例
// 读入整数(跳过中间的所有连续空白符)
int a, b;
scanf("%d%d", &a, &b);

// 读入字符串到c风格字符串(读到空白符停止)
char s[10];
scanf("%s", s);

std::getline

在标头 <string> 定义。

getline 从输入流读取字符并将它们放进字符串。

用法

getline(input, str, delim='\n');

参数

  • input:获取数据来源的流
  • str:放置数据的目标字符串
  • delim:分隔字符

返回值input

示例
// 读取一整行到c++风格字符串
string s;
getline(cin, s);

cin.getline

std::basic_istream<CharT,Traits>::getline

从流释出字符,直至行尾或指定的分隔符 delim

用法

getline(s, count, delim='\n');

参数

  • s:指向要存储字符到的字符串的指针
  • counts 所指向的字符串的大小
  • delim:释出所终止于的分隔字符,释出但不存储它

返回值*this

示例
// 读取一整行到c风格字符串
char s[10];
cin.getline(s, 10);

cin.get

std::basic_istream<CharT,Traits>::get

从流释出字符或多个字符。

用法

int get();

读取一个字符,若可用则返回它。否则,返回 Traits::eof() 并设置 failbiteofbit

返回值

  • 释出的字符或 Traits::eof()

get(ch);

读取一个字符,而若可用则存储于 ch

参数

  • ch:到要写入结果到的字符的引用

返回值

  • *this

get(s, count, delim='\n');

从流中提取字符并将它们作为字符串存储在 s 中,直到提取 n1n-1 个字符或遇到分隔符。分隔符是换行符 '\n'delim(如果指定了此参数),如果找到分隔符,则不会从输入序列中提取,而是保留在那里作为要从流中提取的下一个字符。

参数

  • s:指向要存储结果到的字符串的指针
  • counts 所指向的字符串的大小
  • delim:用以停止释出的分隔字符,不释出且不存储它

返回值

  • *this

get(strbuf, delim='\n');

从流中提取字符并将它们插入到由流缓冲区对象 strbuf 控制的输出序列中,一旦这样的插入失败或在输入序列中遇到分隔符就停止,分隔符是 '\n'delim(如果指定了此参数)。

参数

  • strbuf:要读取内容到的流缓冲
  • delim:用以停止释出的分隔字符,不释出且不存储它

返回值

  • *this
示例
// 读取单字符
char c;
c = cin.get();
cin.get(c);

// 读取c风格字符串
char s[20];
cin.get(s, 10, '.'); // 读取前 9 个字符或直到分隔符 '.'

fgets

在标头 <stdio.h> 定义。

从给定文件流读取最多 count - 1 个字符并将它们存储于 str 所指向的字符数组。若文件尾出现或发现换行符则终止分析,后一情况下 str 将包含一个换行符。若读入字节且无错误发生,则紧随写入到 str 的最后一个字符后写入空字符。

用法

fgets(str, count, stream);

参数

  • str:指向 char 数组元素的指针
  • count:写入的最大字符数(典型的为 str 的长度)
  • stream:读取数据来源的文件流

返回值

成功时为 str ,失败时为空指针。
若遇到文件尾条件导致了失败,则设置 stream 上的文件尾指示器(见 feof() )。这仅若它导致未读取字符才是失败,该情况下返回空指针且不改变 str 所指向数组的内容(即不以空字符覆写首字节)。
若某些其他错误导致了失败,则设置 stream 上的错误指示器(见 ferror() )。 str 所指向的数组内容是不确定的(甚至可以不是空终止)。

示例
// 从c标准输入中读入
char s[1010];
fgets(s, 1000, stdin);

fread

在标头 <stdio.h> 定义。

从给定输入流 stream 读取至多 count 个对象到数组 buffer 中。

用法

fread(buffer, size, count, stream);

参数

  • buffer:指向要读取的数组中首个对象的指针
  • size:每个对象的字节大小
  • count:要读取的对象数
  • stream:读取来源的输入文件流

返回值

成功读取的对象数,若出现错误或读到文件尾,则可能小于 count
sizecount00 ,则 fread 返回 00 且不进行其他动作。
fread 不区别文件尾和错误,而调用者必须用 feofferror 鉴别出现者为何。

示例
// 从c标准输入中读入
char s[1000];
fread(s, 1, 1000, stdin);

常见输入样例

单组输入

输入整数、字符、…
int a, b;
scanf("%d%d", &a, &b);
int a, b;
cin >> a >> b;
输入字符串(空白符分隔)
char s[100];
scanf("%s", s);
string s;
cin >> s;

多组输入

给定数量
int n;
scanf("%d", &n);
while (n--) {
    int a, b;
    scanf("%d%d", &a, &b);
}
int n;
cin >> n;
while (n--) {
    int a, b;
    cin >> a >> b;
}
读到文件尾结束
int a, b;
while (~scanf("%d%d", &a, &b)) {
    // ...
}
int a, b;
while (cin >> a >> b) {
    // ...
}
读到特殊输入结束

输入多组数,两个一组,读入单个 -1 结束:

int a, b;
while (~scanf("%d%d", &a, &b) && a != -1) {
    // ...
}
int a, b;
while (cin >> a >> b && a != -1) {
    // ...
}

整行输入

输入单行到字符串
char s[100];
cin.getline(s, 100);
string s;
getline(cin, s);
char s[100];
fgets(s, 100, stdin);
输入多行直到文件尾结束
vector<string> ss;
string s;

while (getline(cin, s)) {
    ss.push_back(s);
}
char ss[N][N];
int idx = 0;

while (cin.getline(ss[cnt], N)) ++idx;
vector<char*> ss;
char s[N];

while (cin.getline(s, N)) {
    char* tmp = new char[N];
    strcpy(tmp, s);
    ss.push_back(tmp);
}

C++ 标准输入输出提速

太长不看版

int _ = []() {
    ios::sync_with_stdio(false);
    cin.tie(NULL), cout.tie(NULL);
    return 0;
}();

取消 iostream 与 stdio 的同步

C++ 中 的 iostream 为了确保能和 C 中的 stdio 在同一文件操作时按顺序进行,而进行了一些额外的同步操作,这些操作会对性能造成一些影响。
取消同步后,性能可能会得到提升,但之后不应再与 stdio 混用,否则读写顺序上不能保证。

取消同步:

std::ios::sync_with_stdio(false);

解除输入输出流的关联

C++ 中的 输入流 std::cin 默认是关联到 输出流 std::cout 的,任何试图从输入流 读取数据的操作都会先刷新关联的 输出流,这样输出流的缓冲区就会在输入前得到刷新。
关联输入输出流意味着所有输出(包括用户提示等)都会在输入前被打印出来,而不会有在进行输入时之前调用的输出没有得到显示的情况发生。

解除输入输出流的关联来提升性能:

std::cin.tie(NULL);

重新关联:

std::cin.tie(&std::cout);

跳过末尾空白符

#include <limits>

cin.ignore(numeric_limits<streamsize>::max(), '\n');

重定向输入输出

// 将标准输入流重定向到 in.txt 文件
freopen("in.txt", "r", stdin);
// 将标准输出流重定向到 out.txt 文件
freopen("out.txt", "w", stdout); 

__int128 及其输入输出

简介

_int128 即占用 128 字节的整数存储类型。范围为:[2127,21271][-2^{127}, 2^{127}-1] (最大约 1.7e391.7e39),如果使用了 unsigned __int128,则范围变成 [0,2128][0, 2^{128}]

注意:__int128 仅 64 位 gcc/g++ 支持,并不在 C++ 标准中!

由于其不在 C++ 标准内,没有配套的 printfscanfcincout 输入输出,只能手写。

输入

快读:

__int128 read() {
    __int128 x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

输出

快写:

void print(__int128 x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) print(x / 10);
    putchar(x % 10 + '0');
}

Java

输入常用到的方法

Scanner

java.util 包中定义。

一个简单的文本扫描程序,可以使用正则表达式解析基本类型和字符串。

使用
import java.util.Scanner;

Scanner in = new Scanner(System.in);
常用方法
方法 说明
boolean hasNext() 判断输入是否还有下一个输入项,若有,则返回 true,反之 false
String next() 读取下一个输入项(读到分隔符停止),并返回为字符串
double nextDouble() 读取下一个输入项(读到分隔符停止),并返回为双精度浮点数
double nextInt() 读取下一个输入项(读到分隔符停止),并返回为整数
double nextLong() 读取下一个输入项(读到分隔符停止),并返回为长整型数
String nextLine() 读取下一行输入,并返回为字符串

BufferedReader + StreamTokenizer

java.io 包中定义。

使用注意
  • StreamTokenizer 源码存在 Type,该 Type 根据你输入内容来决定类型,倘若你输入类似于 123oi数字开头的字符串,它会强制认为你的类型是 double 类型,因此在读入中以 double 类型去读 String 类型便会抛出异常。
  • StreamTokenizer 在读入 101410^{14} 以上大小的数字会丢失精度。
使用
import java.io.*;

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
常用方法
  • 判断是否读到文件尾:

    if (in.nextToken() == in.TT_EOF) // ...
    
  • 读入一个字符串(读到空白符停止):

    in.nextToken();
    String s = in.sval;
    
  • 读入一个整数(读到空白符停止):

    in.nextToken();
    int n = (int) in.nval;
    
  • 读入一个长整型(读到空白符停止)(读入 1e14 以上大小的数字会丢失精度。):

    in.nextToken();
    int n = (long) in.nval;
    
  • 读入一个双精度浮点型(读到空白符停止)(读入 1e14 以上大小的数字会丢失精度。):

    in.nextToken();
    double n = in.nval;
    
  • 读入一行字符串(读到换行符停止):

    String line = br.readLine();
    
模板
import java.util.*;

public static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in), 32768));
public static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
public static double nextDouble() throws IOException { in.nextToken(); return in.nval; }
public static float nextFloat() throws IOException { in.nextToken(); return (float) in.nval; }
public static int nextInt() throws IOException { in.nextToken(); return (int) in.nval; }
public static String next() throws IOException { return in.sval; }
public static long nextLong() throws Exception { in.nextToken(); return (long) in.nval;}

Kattio + StringTokenizer

最常用的方法之一是使用来自 Kattis 的 Kattio.java 来提高 IO 效率。1这个方法会将 StringTokenizerPrintWriter 包装在一个类中方便使用。而在具体进行解题的时候(假如赛会/组织方允许)可以直接使用这个模板。

下方即为应包含在代码中的 IO 模板,由于 Kattis 的原 Kattio 包含一些并不常用的功能,下方的模板经过了一些调整(原 Kattio 使用 MIT 作为协议)。

模板
class Kattio extends PrintWriter {
    private BufferedReader r;
    private StringTokenizer st;
    // 标准 IO
    public Kattio() { this(System.in, System.out); }
    public Kattio(InputStream i, OutputStream o) {
        super(o);
        r = new BufferedReader(new InputStreamReader(i));
    }
    // 文件 IO
    public Kattio(String intput, String output) throws IOException {
        super(output);
        r = new BufferedReader(new FileReader(intput));
    }
    // 在没有其他输入时返回 null
    public String next() {
        try {
            while (st == null || !st.hasMoreTokens())
                st = new StringTokenizer(r.readLine());
            return st.nextToken();
        } catch (Exception e) {}
        return null;
    }
    public int nextInt() { return Integer.parseInt(next()); }
    public double nextDouble() { return Double.parseDouble(next()); }
    public long nextLong() { return Long.parseLong(next()); }
}
使用
class Test {
    public static void main(String[] args) {
        Kattio io = new Kattio();
        // 字符串输入
        String str = io.next();
        // int 输入
        int num = io.nextInt();
        // 输出
        io.println("Result");
        // 请确保关闭 IO 流以确保输出被正确写入
        io.close();
    }
}

常见输入样例

单组输入

输入整数、小数、…
Scanner in = new Scanner(System.in);

int a = in.nextInt();
double b = in.nextDouble();
String s = in.next();
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

in.nextToken();
int a = (int) in.nval;
in.nextToken();
double n = in.nval;
in.nextToken();
int s = in.sval;
Kattio io = new Kattio();

int a = io.nextInt();
double b = io.nextDouble();
String s = io.next();
输入字符串(空白符分隔)
Scanner in = new Scanner(System.in);

String s = in.next();
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

in.nextToken();
int s = in.sval;
Kattio io = new Kattio();

String s = io.next();

多组输入

给定数量
Scanner in = new Scanner(System.in);

int n = in.nextInt();
while (n-- > 0) {
    int a = in.nextInt();
    int b = in.nextInt();
}
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

in.nextToken();
int n = (int) in.nval;
while (n-- > 0) {
    in.nextToken();
	int a = (int) in.nval;
    in.nextToken();
	int b = (int) in.nval;
}
Kattio io = new Kattio();

int n = io.nextInt();
while (n-- > 0) {
    int a = io.nextInt();
    int b = io.nextInt();
}
读到文件尾结束
Scanner in = new Scanner(System.in);

while (in.hasNext()) {
    int a = in.nextInt();
    int b = in.nextInt();
}
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
int a, b;

while (in.nextToken() != in.TT_EOF) {
    a = (int) in.nval;
    b = (int) in.nval;
}
Kattio io = new Kattio();
        
String tmp;
while ((tmp = io.next()) != null) {
    int a = Integer.parseInt(tmp);
    int b = io.nextInt();
}
读到特殊输入结束

输入多组数,两个一组,读入单个 -1 结束:

Scanner in = new Scanner(System.in);

int a, b;
while (in.hasNext() && (a = in.nextInt()) != -1) {
    b = in.nextInt();
}
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

int a, b;
while (in.nextToken() != in.TT_EOF && (a = (int) in.nval) != 0) {
    in.nextToken();
    b = (int) in.nval;
}
Kattio io = new Kattio();
        
String tmp;
int a, b;
while ((tmp = io.next()) != null && (a = Integer.parseInt(tmp)) != -1) {
    b = io.nextInt();
}

整行输入

输入单行到字符串
Scanner in = new Scanner(System.in);

String s = in.nextLine();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

String s = in.readLine();
输入多行直到文件尾结束
Scanner in = new Scanner(System.in);
String[] s = new String[N];
int idx = 0;

while (in.hasNextLine()) s[++idx] = in.nextLine();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String[] s = new String[N];
int idx = 0;

while (in.ready()) s[++idx] = in.readLine();

参考

2

评论区