读写加速

机考输入加速2.0(fread版):全类型快读总结

基于fread的快读2.0核心是“全局缓冲区预读+分类型解析”,解决机考中int、long long(LL)、字符串(含/不含空格)的高效读取问题,配合快写可实现输入输出双加速,彻底避免IO超时。以下是完整实现、示例用法及机考注意事项。

一、核心原理

通过fread一次性将输入数据读入全局内存缓冲区(默认1MB,足够覆盖绝大多数机考输入规模),后续读取各类型数据时直接从缓冲区取字符,避免频繁系统调用(IO操作中最耗时的环节)。每个数据类型的快读函数均基于缓冲区实现,逻辑独立且易用。

二、完整实现(加速2.0模板)

#include <cstdio>
#include <cstring> // 用于memset(字符串快读用)
using namespace std;

// -------------------------- 全局缓冲区(所有快读函数共用) --------------------------
const int BUF_SIZE = 1 << 20; // 1MB缓冲区(机考推荐大小,可按需调至1<<25=32MB)
char buf[BUF_SIZE];           // 存储预读的输入数据
int buf_ptr = 0;              // 缓冲区当前读取指针(指向下次要读的字符)
int buf_len = 0;              // 缓冲区中已预读的数据长度

// 辅助函数:从缓冲区取1个字符(缓冲区空则自动用fread补满)
inline char my_getc() {
    if (buf_ptr >= buf_len) { // 缓冲区已读完,重新预读
        buf_len = fread(buf, 1, BUF_SIZE, stdin);
        buf_ptr = 0;
        if (buf_len == 0) return EOF; // 输入结束(避免死循环)
    }
    return buf[buf_ptr++]; // 返回当前字符并移动指针
}

// -------------------------- 1. 快读int(处理正负整数,跳过空白符) --------------------------
inline int read_int() {
    int x = 0;
    int sign = 1; // 符号位(1为正,-1为负)
    char ch = my_getc();

    // 第一步:跳过所有空白符(空格、换行、制表符等,机考输入常含多余空白)
    while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') {
        ch = my_getc();
    }

    // 第二步:处理负号(若有)
    if (ch == '-') {
        sign = -1;
        ch = my_getc();
    }

    // 第三步:读取数字并累积
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + (ch - '0');
        ch = my_getc();
    }

    return x * sign;
}

// -------------------------- 2. 快读long long(LL,同int逻辑,仅返回类型不同) --------------------------
inline long long read_LL() {
    long long x = 0;
    int sign = 1;
    char ch = my_getc();

    // 跳过空白符
    while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') {
        ch = my_getc();
    }

    // 处理负号
    if (ch == '-') {
        sign = -1;
        ch = my_getc();
    }

    // 累积数字(用long long避免溢出)
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + (ch - '0');
        ch = my_getc();
    }

    return x * sign;
}

// -------------------------- 3. 快读无空格字符串(如单词、标识符,遇空白符停止) --------------------------
// 功能:读取不含空格、换行的字符串,存入str数组,返回字符串长度
// 参数:str:目标字符数组(需提前分配足够空间,机考建议1e5+);max_len:str最大长度(防止溢出)
inline int read_str(char str[], int max_len) {
    int len = 0;
    char ch = my_getc();

    // 第一步:跳过前导空白符(如输入开头的空格、换行)
    while (ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t') {
        ch = my_getc();
    }

    // 第二步:读取字符(直到遇到空白符或达到数组最大长度)
    while (ch != ' ' && ch != '\n' && ch != '\r' && ch != '\t' && ch != EOF && len < max_len - 1) {
        str[len++] = ch;
        ch = my_getc();
    }

    // 第三步:添加字符串结束符(C风格字符串必需)
    str[len] = '\0';
    return len;
}

// -------------------------- 4. 快读有空字符串(如整行输入,含空格,遇换行停止) --------------------------
// 功能:读取一整行(包括空格),存入str数组,自动过滤末尾的'\n'
// 参数:str:目标字符数组;max_len:str最大长度
inline int read_line(char str[], int max_len) {
    int len = 0;
    char ch = my_getc();

    // 读取字符(直到遇到换行或EOF,且不超过数组最大长度)
    while (ch != '\n' && ch != EOF && len < max_len - 1) {
        str[len++] = ch;
        ch = my_getc();
    }

    // 处理换行符(若存在,跳过,避免下一次读取受影响)
    if (ch == '\n') {
        buf_ptr++; // 跳过'\n'(my_getc()已读过ch,需手动移动指针)
    }

    // 添加字符串结束符
    str[len] = '\0';
    return len;
}

// -------------------------- 配套快写(机考输出加速必备,避免输出超时) --------------------------
char out_buf[BUF_SIZE]; // 输出缓冲区
int out_ptr = 0;         // 输出缓冲区指针

// 辅助:写入单个字符到缓冲区
inline void putc(char c) {
    if (out_ptr >= BUF_SIZE) { // 缓冲区满,批量写入stdout
        fwrite(out_buf, 1, out_ptr, stdout);
        out_ptr = 0;
    }
    out_buf[out_ptr++] = c;
}

// 快写int
inline void write_int(int x) {
    if (x < 0) { putc('-'); x = -x; }
    if (x == 0) { putc('0'); return; }
    char stk[20]; int top = 0;
    while (x) { stk[top++] = x % 10 + '0'; x /= 10; }
    while (top--) { putc(stk[top]); }
}

// 快写long long
inline void write_LL(long long x) {
    if (x < 0) { putc('-'); x = -x; }
    if (x == 0) { putc('0'); return; }
    char stk[20]; int top = 0;
    while (x) { stk[top++] = x % 10 + '0'; x /= 10; }
    while (top--) { putc(stk[top]); }
}

// 快写字符串
inline void write_str(const char str[]) {
    int i = 0;
    while (str[i] != '\0') {
        putc(str[i++]);
    }
}

// 快写换行符
inline void write_endl() {
    putc('\n');
}

// 关键:刷新输出缓冲区(程序结束前必须调用,否则内容可能残留)
inline void flush_out() {
    if (out_ptr > 0) {
        fwrite(out_buf, 1, out_ptr, stdout);
        out_ptr = 0;
    }
}

三、机考示例用法(完整流程)

以下示例模拟机考常见场景:读取多种类型输入,处理后用快写输出,覆盖所有快读/快写函数的使用。

int main() {
    // -------------------------- 1. 读取int示例:读n个整数,输出总和 --------------------------
    int n = read_int();
    int sum_int = 0;
    for (int i = 0; i < n; i++) {
        int x = read_int();
        sum_int += x;
    }
    write_str("int总和:");
    write_int(sum_int);
    write_endl();

    // -------------------------- 2. 读取LL示例:读m个长整数,输出最大值 --------------------------
    int m = read_int();
    long long max_LL = -1e18;
    for (int i = 0; i < m; i++) {
        long long x = read_LL();
        if (x > max_LL) max_LL = x;
    }
    write_str("LL最大值:");
    write_LL(max_LL);
    write_endl();

    // -------------------------- 3. 读取无空格字符串示例:读2个单词,输出拼接结果 --------------------------
    char str1[100], str2[100];
    read_str(str1, 100); // 读第一个单词(如"Hello")
    read_str(str2, 100); // 读第二个单词(如"World")
    write_str("拼接字符串:");
    write_str(str1);
    write_str(" ");
    write_str(str2);
    write_endl();

    // -------------------------- 4. 读取有空字符串示例:读1行句子,输出长度 --------------------------
    char line[1000];
    int line_len = read_line(line, 1000); // 读整行(如"I love programming")
    write_str("句子长度:");
    write_int(line_len);
    write_endl();
    write_str("你输入的句子:");
    write_str(line);
    write_endl();

    // -------------------------- 关键:刷新输出缓冲区 --------------------------
    flush_out();
    return 0;
}

输入示例:

3 1 2 3          // n=3,3个int:1,2,3
2 10000000000 5  // m=2,2个LL:1e10,5
Hello World       // 2个无空格字符串
I love programming// 1行有空字符串

输出示例:

int总和:6
LL最大值:10000000000
拼接字符串:Hello World
句子长度:18
你输入的句子:I love programming

四、机考注意事项(避坑指南)

  1. 缓冲区刷新必做:快写的flush_out()必须在main函数结束前调用,否则缓冲区中残留的输出内容会丢失(如最后几行结果不显示)。
  2. 字符串数组大小read_strread_line的字符数组需提前分配足够空间(机考建议至少1e5,避免输入过长导致数组越界)。
  3. 空白符处理:所有快读函数均自动跳过前导空白符(空格、换行),无需手动处理输入中的多余空白,适配机考输入格式。
  4. 类型匹配read_int对应intread_LL对应long long,避免混用(如用read_int读1e18会溢出)。
  5. 编译器兼容性:代码基于C++标准,机考中GCC、Clang、MSVC均兼容,无需额外配置。

五、优势总结(为何称“加速2.0”)

对比维度 普通cin/cout(关闭同步) 基础getchar/putchar快读 本版fread快读2.0
系统调用次数 多(每次读/写均可能触发) 较多(每次1个字符) 极少(1次预读)
支持类型 全,但慢 仅int/LL int/LL/字符串全支持
机考适配性 易超时(大数据量) 部分场景适用 全场景高效,无超时风险
易用性 低(需重复写逻辑) 高(模板化,直接调用)

本模板可直接作为机考“输入输出工具库”,复制到代码开头即可使用,彻底解决IO效率问题,专注算法逻辑实现。

posted @ 2025-09-07 11:15  .N1nEmAn  阅读(27)  评论(0)    收藏  举报