LOADING

加载过慢请开启缓存,浏览器默认开启

C++ Fast-IO

写了一个 Fast-IO 快读/快写库

支持基本类型与 __int128_t 读写,指针地址写入,和 cin/cout 用法类似

虽然相对于原版函数式 fread 快读会有点慢,但还是比 getchar/putchar 要快的

Fast-IO 用了 C++20 Concepts,用以下编译参数即可

g++ <file>.cpp -o <file> -std=c++20 -Wall -O2 -O3 -Ofast

目录解释

fastio.cpp         # 测试文件
fastio.h           # Fast-IO 库源文件
fastio.in          # 输入测试文件
fastio.speed.in    # 速度测试文件
README.md          # README

使用

  • using namespace fastio;

    使用 Fast-IO

  • using namespace fastio::syms;

    使用输入输出符号,如 endl

  • rs >> x;

    读取 x

  • rs >> x >> y >> z;

    读取 x, y, z

  • c = rs.get();

    读取一个字符到 c

  • rs.read(x, y, z);

    读取 x, y, z

  • x = rs.read<int>();

    读取一个 int 类型的数据到 x

  • x = rs.read<T>();

    读取一个 T 类型的数据到 x

  • x = rs.read<char*>(n); x = rs.read<const char*>(n);

    读取一个最长为 n 的字符串到 s,s 只能为字符指针,不能为数组

  • rs >> s;

    读取一个字符串 s,s 只能为字符数组,不能为指针

  • rs.getline(s, n);

    读取一行到 s,最多读取 n 个字符

  • rs.getline(s, n, c);

    读取字符到 s,最多读取 n 个字符,一直读到 c 停止

  • rs.ignore();

    忽略一行

  • rs.ignore(n);

    忽略一行,最多忽略 n 个字符(若 n 为 $2147483647$ 则等于 rs.ignore();

  • rs.ignore(n, c);

    忽略字符到 c 停止,最多忽略 n 个字符

  • rs.seek();

    移动读取指针到开始

  • rs.eof;

    rs 是否读到末尾

  • rs.fail;

    rs 是否发生错误(读到末尾后,继续读取)

  • !rs; (bool)rs;

    分别等于 rs.fail; !rs.fail;

  • while (rs >> ...);

    一直读取直到末尾

  • rs >> bin; rs >> oct; rs >> dec; rs >> hex;

    按 2 8 10 16 进制读取整数

  • rs >> skipws;

    忽略前导空格

  • rs.setbase(base);

    按 base 进制读取整数,超出范围默认 10 进制 $(2 \le base \le 36)$

  • rs >> reset;

    取消前面的所有设置

  • ws << x;

    写入 x

  • ws << x << y << z;

    写入 x, y, z

  • ws.put(c);

    写入一个字符 c

  • ws.write(x, y, z);

    写入 x, y, z

  • ws.flush();ws << flush;

    刷新流

  • ws << endl;

    写入换行

  • ws << ends;

    写入空格

  • ws << boolalpha;

    设置写入 bool 时用 true/false

  • ws << noboolalpha;

    设置写入 bool 时用 0/1

  • ws << showpos;

    设置写入非负数(不包括 bool)时前面加 +

  • ws << noshowpos;

    设置写入非负数(不包括 bool)时前面不加 +

  • ws << showpoint;

    设置写入浮点数时严格保留 setprecision 时设置的位数

  • ws << noshowpoint;

    设置写入浮点数时不严格保留 setprecision 时设置的位数

  • ws << bin; ws << oct; ws << dec; ws << hex;

    按 2/8/10/16 进制写入整数

  • ws << lowercase;

    按大于 10 的进制写入整数时,字母大写(默认小写)

  • ws << uppercase;

    按大于 10 的进制写入整数时,字母小写(默认小写)

  • ws << showbase;

    写入 8/16 进制的整数时,在前面显示 0/0x

  • ws << noshowbase;

    写入 8/16 进制的整数时,不在前面显示 0/0x

  • ws << setbase(base);

    按 base 进制写入整数,超出范围默认 10 进制 $(2 \le base \le 36)$

  • ws << unitbuf;

    设置每写入一个数据就刷新流

  • ws << nounitbuf;

    设置超过最大限制 SIZ 时才刷新流(SIZ 为一个 const 变量)

  • ws << setw(width);

    设置下一次写入宽度若小于 width,就填补字符(下一次写入重置)

  • ws << setfill(c);

    设置 setw 填补的字符,默认为空格

  • ws << setprecision(precision);

    设置浮点数保留位数,默认保留 3 位

  • ws << reset;

    取消前面的所有设置

  • rstream rs;

    创建读流(已经有默认流 rs 了,会发生错误)

  • wstream ws;

    创建写流(已经有默认流 ws 了,会发生错误)

  • rfstream rfs(dir);

    创建文件读流,从 dir 路径文件读取(也可以是 FILE* 文件指针),和普通读流用法相同

  • wfstream wfs(dir);

    创建文件写流,写入 dir 路径的文件(也可以是 FILE* 文件指针),和普通写流用法相同

  • sstream ss;

    创建字符串流,可读写,读取后不会删除数据,会移动读取指针

  • ss.str();

    获取字符串流的数据

  • ss.str(s);

    将字符串流的数据替换为 s 并移动读取指针到开始

接口

  • 重载运算符

    注意要用 fastio::interface::rstream fastio::interface::wstream 来重载

    以下是一些示例程序

    // overload std::string read/write
    auto &operator<<(fastio::interface::rstream &rs, string &s) {
        static char buf[1005];
        return rs >> buf, s = buf, rs;
    }
    auto &operator<<(fastio::interface::wstream &ws, const string s) { return ws << s.c_str(); }
    
    // overload (,) (;) (.) (?) (!) write
    enum punctuation { comma, simicolon, fullstop, question, exclamatory };
    auto &operator<<(fastio::interface::wstream& ws, const punctuation s) {
        if (s == comma) ws.put(',');
        else if (s == simicolon) ws.put(';');
        else if (s == fullstop) ws.put('.');
        else if (s == question) ws.put('?');
        else if (s == exclamatory) ws.put('!');
        return rs;
    }
    
    // overload std::vector<T> read/write
    template<class T> auto& operator<<(fastio::interface::rstream& rs, const vector<T>& v) {
        v.clear();
        size_t n = rs.read<size_t>();
        for (T x; n--;) {
            rs >> x;
            v.push_back(x);
        }
        return ws;
    }
    template<class T> auto& operator<<(fastio::interface::wstream& ws, const vector<T> v) {
        for (auto p = v.begin(); p != v.end(); ++p) {
            ws << *p;
            if (p != v.end() - 1) ws.put(',');
        }
        return ws;
    }
    

Code

#include <cctype>
#include <climits>
#include <cmath>
#include <concepts>
#include <cstdio>
#include <cstring>
#define SIZ 0xfffff
namespace fastio {
namespace syms {
enum symbol {
    endl,
    ends,
    flush,
    skipws,
    boolalpha,
    noboolalpha,
    showpos,
    noshowpos,
    showpoint,
    noshowpoint,
    showbase,
    noshowbase,
    bin,
    oct,
    dec,
    hex,
    uppercase,
    lowercase,
    unitbuf,
    nounitbuf,
    reset
};
// clang-format off
struct setw { int data; };
struct setfill { char data; };
struct setprecision { int data; };
struct setbase { int data; };
// clang-format on
}  // namespace syms
namespace interface {
using namespace syms;
template <typename T>
concept integer_t = std::integral<T> || std::same_as<T, __int128_t> || std::same_as<T, __uint128_t>;
template <typename T>
concept signed_integer_t = std::signed_integral<T> || std::same_as<T, __int128_t>;
template <typename T>
concept float_t = std::floating_point<T>;
template <typename T>
concept string_t = std::same_as<T, char*> || std::same_as<T, const char*>;
template <typename T>
concept notstring_t = (!string_t<T>);
class noncopyable {
  private:
    noncopyable(const noncopyable&) = delete;
    void operator=(const noncopyable&) = delete;

  protected:
    noncopyable() = default;
    ~noncopyable() = default;
};
class rstream : public noncopyable {
    int base = 10;
    bool pre;
    char prech;
    bool isnum(char ch) {
        return (isdigit(ch) && ch < '0' + base) || (isupper(ch) && ch < 'A' - 10 + base) ||
               (islower(ch) && ch < 'a' - 10 + base);
    }
    bool iseof(char ch) { return !~ch; }
    int tonum(char ch) { return ch - (isdigit(ch) ? '0' : isupper(ch) ? 'A' - 10 : 'a' - 10); }
    void reads(char* s, int N) {
        int t = 0;
        char ch;
        while (!iseof(ch = get()) && isspace(ch))
            ;
        if (eof) return (void)(fail = 1);
        while (isgraph(ch)) {
            if (t < N - 1) s[t++] = ch;
            ch = get();
        }
        s[t] = '\0', pre = 1;
    }

  protected:
    virtual char vget() = 0;
    virtual void vseek() = 0;

  public:
    bool eof = 0, fail = 0;
    rstream() = default;
    char get() {
        if (pre) pre = 0;
        else prech = vget();
        if (iseof(prech)) eof = 1;
        return prech;
    }
    operator bool() { return !fail; }
    template <integer_t T> rstream& operator>>(T& x) {
        x = 0;
        bool t = 0, f = signed_integer_t<T>;
        char ch;
        while (!iseof(ch = get()) && !isnum(ch))
            if (f && isgraph(ch)) t = ch == '-';
        if (eof) return fail = 1, *this;
        while (isnum(ch)) x = x * base + tonum(ch), ch = get();
        if (t) x = -x;
        pre = 1;
        return *this;
    }
    rstream& operator>>(float_t auto& x) {
        x = 0;
        bool t = 0;
        long double k = 1;
        char ch;
        while (!iseof(ch = get()) && !isdigit(ch))
            if (isgraph(ch)) t = ch == '-';
        if (eof) return fail = 1, *this;
        while (isdigit(ch)) x = x * 10 + ch - '0', ch = get();
        if (ch == '.')
            while (isdigit(ch = get())) x += (double)(ch - '0') / (k *= 10);
        if (t) x = -x;
        pre = 1;
        return *this;
    }
    rstream& operator>>(char& ch) {
        while (!iseof(ch = get()) && isspace(ch))
            ;
        if (eof) fail = 1, ch = 0;
        return *this;
    }
    template <int N> rstream& operator>>(char (&s)[N]) {
        reads(s, N);
        return *this;
    }
    rstream& operator>>(bool& f) {
        long long x;
        *this >> x, f = x;
        return *this;
    }
    rstream& operator>>(const symbol s) {
        if (s == bin) base = 2;
        else if (s == oct) base = 8;
        else if (s == dec || s == reset) base = 10;
        else if (s == hex) base = 16;
        else if (s == skipws) *this >> *new char, pre = 1;
        return *this;
    }
    rstream& operator>>(const setbase sp) {
        base = sp.data;
        if (base < 2 || base > 36) base = 10;
        return *this;
    }
    rstream& ignore(int N = INT_MAX, char delim = '\n') {
        char ch;
        if (N == INT_MAX)
            while (!iseof(ch = get()) && ch != delim)
                ;
        else
            while (N-- && !iseof(ch = get()) && ch != delim)
                ;
        if (eof) return fail = 1, *this;
        return *this;
    }
    rstream& getline(char* s, int N, char delim = '\n') {
        int t = 0;
        char ch;
        while (N-- && !iseof(ch = get()) && ch != delim) s[t++] = ch;
        if (!t) return fail = 1, *this;
        if (delim == '\n' && s[t - 1] == '\r') --t;
        s[t] = '\0';
        return *this;
    }
    rstream& seek() {
        vseek();
        return *this;
    }
    rstream& read(auto... args) { return (*this >> ... >> args); }
    template <notstring_t T> const T read() {
        T x;
        return *this >> x, x;
    }
    template <string_t T> char* read(int N) {
        char* s = new char[N]{};
        reads(s, N);
        return s;
    }
};
class wstream : public noncopyable {
    int setw = 0, precision = 6, base = 10;
    bool boolalpha = 0, showpos = 0, showpoint = 0, showbase = 0, kase = 0, unitbuf = 0;
    char setfill = ' ';
    long double eps = 1e-6, EPS = 1e6;
    void update() {
        long double x = 0.1, y = 10;
        int k = precision;
        for (eps = EPS = 1; k; k >>= 1, x *= x, y *= y)
            if (k & 1) eps *= x, EPS *= y;
    }
    char tochr(int x) { return x + (x < 10 ? '0' : kase ? 'A' - 10 : 'a' - 10); }
    void fill(int N) {
        setw = 0;
        vfill(setfill, N);
    }

  protected:
    virtual void vflush() = 0;
    virtual void vfill(char, int) = 0;
    virtual void vput(char) = 0;
    virtual void vputs(const char*, int = -1) = 0;

  public:
    wstream() = default;
    wstream& flush() {
        vflush();
        return *this;
    }
    wstream& put(char ch) {
        vput(ch);
        return *this;
    }
    wstream& operator<<(integer_t auto x) {
        static char buf[205], *end = buf + 200;
        char* p = end;
        bool t = x < 0;
        if (!x) *p-- = '0';
        while (x) *p-- = tochr(t ? -(x % -base) : x % base), x /= base;
        if (showbase && base == 16) *p-- = kase ? 'X' : 'x', *p-- = '0';
        else if (showbase && base == 8) *p-- = '0';
        if (t || showpos) *p-- = t ? '-' : '+';
        fill(setw - (end - p));
        vputs(p + 1, end - p);
        if (unitbuf) flush();
        return *this;
    }
    template <float_t T> wstream& operator<<(T x) {
        static char buf[1005], BUF[1005], *end = buf + 1000, *END = BUF + 1000;
        char *p = end, *q = END, *d = END;
        bool t = x < 0;
        if (t) x = -x;
        T a = std::floor(x), b = std::round((x - a) * EPS);
        if (b >= EPS) ++a, b = 0;
        if (a < eps) *p-- = '0';
        while (a >= eps) *p-- = (int)std::fmod(a, 10) + '0', a = std::floor(a / 10);
        while (b >= eps) *q-- = (int)std::fmod(b, 10) + '0', b = std::floor(b / 10);
        if (q != END || showpoint) *q-- = '.';
        while (*d == '0' && d != q && !showpoint) --d;
        if (showpos) *p-- = t ? '-' : '+';
        fill(setw - (end - p) - (d - q));
        vputs(p + 1, end - p);
        vputs(q + 1, d - q);
        if (unitbuf) flush();
        return *this;
    }
    wstream& operator<<(char ch) {
        fill(setw - 1);
        put(ch);
        if (unitbuf) flush();
        return *this;
    }
    wstream& operator<<(const char* s) {
        int N = strlen(s);
        fill(setw - N);
        vputs(s);
        if (unitbuf) flush();
        return *this;
    }
    wstream& operator<<(bool f) {
        if (boolalpha) {
            fill(setw - (f ? 4 : 5));
            vputs(f ? "true" : "false");
        } else {
            fill(setw - 1);
            put(f ? '1' : '0');
        }
        if (unitbuf) flush();
        return *this;
    }
    wstream& operator<<(const void* p) {
        int b = base, f = showbase;
        base = 16, showbase = 1;
        *this << (size_t)p;
        base = b, showbase = f;
        return *this;
    }
    wstream& operator<<(const symbol s) {
        if (s == endl) put('\n');
        else if (s == ends) put(' ');
        else if (s == syms::flush) flush();
        else if (s == syms::boolalpha) boolalpha = 1;
        else if (s == noboolalpha) boolalpha = 0;
        else if (s == syms::showpos) showpos = 1;
        else if (s == noshowpos) showpos = 0;
        else if (s == syms::showpoint) showpoint = 1;
        else if (s == noshowpoint) showpoint = 0;
        else if (s == syms::showbase) showbase = 1;
        else if (s == noshowbase) showbase = 0;
        else if (s == syms::unitbuf) unitbuf = 1;
        else if (s == nounitbuf) unitbuf = 0;
        else if (s == lowercase) kase = 0;
        else if (s == uppercase) kase = 1;
        else if (s == bin) base = 2;
        else if (s == oct) base = 8;
        else if (s == dec) base = 10;
        else if (s == hex) base = 16;
        else if (s == reset) {
            boolalpha = showpos = showpoint = showbase = kase = unitbuf = 0;
            setfill = ' ', precision = 6, eps = 1e-6, EPS = 1e6, base = 10;
        }
        return *this;
    }
    wstream& operator<<(syms::setw sp) {
        setw = std::max(sp.data, 0);
        return *this;
    }
    wstream& operator<<(const syms::setfill sp) { return setfill = sp.data, *this; }
    wstream& operator<<(const syms::setprecision sp) {
        precision = std::max(sp.data, 0);
        update();
        return *this;
    }
    wstream& operator<<(const syms::setbase sp) {
        base = sp.data;
        if (base < 2 || base > 36) base = 10;
        return *this;
    }
    wstream& write(auto... args) { return (*this << ... << args); }
};
}  // namespace interface
class rstream : public interface::rstream {
    char buf[SIZ], *p = buf, *q = buf;
    char vget() {
        if (p != q) return *p++;
        if (feof(file) || ferror(file)) {
            clearerr(file);
            return EOF;
        }
        if (p == q) {
            q = (p = buf) + fread(buf, 1, SIZ, file);
            if (p == q) return EOF;
        }
        return *p++;
    }
    void vseek() {
        fseek(file, 0, 0);
        p = q = buf;
    }

  protected:
    FILE* file = stdin;

  public:
    ~rstream() { fclose(file); }
};
class rfstream : public rstream {
  public:
    rfstream(FILE* f) { file = f; }
    rfstream(const char* dir) { file = fopen(dir, "r"); }
};
class wstream : public interface::wstream {
    char buf[SIZ], *p = buf;
    void vflush() {
        fwrite(buf, p - buf, 1, file);
        p = buf;
    }
    void vfill(char ch, int N) {
        if (N < 0) return;
        int use = p - buf;
        while (N + use >= SIZ) {
            memset(buf + use, ch, SIZ - use);
            p = buf + SIZ;
            vflush();
            N -= SIZ - use, use = 0;
        }
        memset(buf + use, ch, N);
        p = buf + N + use;
    }
    void vput(char ch) {
        if (p - buf >= SIZ) vflush();
        *p++ = ch;
    }
    void vputs(const char* s, int N = -1) {
        if (N < 0) N = strlen(s);
        int use = p - buf, _len = N;
        while (N + use >= SIZ) {
            memcpy(buf + use, s + _len - N, SIZ - use);
            p = buf + SIZ;
            vflush();
            N -= SIZ - use, use = 0;
        }
        memcpy(buf + use, s + _len - N, N);
        p = buf + N + use;
    }

  protected:
    FILE* file = stdout;

  public:
    ~wstream() { vflush(), fclose(file); }
};
class wfstream : public wstream {
  public:
    wfstream(FILE* f) { file = f; }
    wfstream(const char* dir) { file = fopen(dir, "w"); }
};
class sstream : public interface::rstream, public interface::wstream {
    char *l = nullptr, *r = nullptr, *p = nullptr, *q = nullptr, *t;
    void reserve() {
        int N = r - l;
        if (!N) N = 2;
        t = new char[(N << 1) + 1]{};
        memcpy(t, l, N);
        p = p - l + t, q = q - l + t;
        delete[] l;
        r = (l = t) + (N << 1);
    }
    char vget() { return q == p ? EOF : *q++; }
    void vseek() { q = l; }
    void vflush() {}
    void vfill(char ch, int N) {
        if (N < 0) return;
        while (r - p < N) reserve();
        memset(p, ch, N), p += N;
        eof = fail = 0;
    }
    void vput(char ch) {
        if (p == r) reserve();
        *p++ = ch;
        eof = fail = 0;
    }
    void vputs(const char* s, int N = -1) {
        if (N < 0) N = strlen(s);
        while (r - p < N) reserve();
        memcpy(p, s, N), p += N;
        eof = fail = 0;
    }

  public:
    sstream() {
        delete[] l;
        r = (p = q = l = new char[2]{}) + 1;
    }
    sstream(const char* s) { str(s); }
    ~sstream() { delete[] l; }
    const char* str() { return l; }
    void str(const char* s) {
        int N = strlen(s);
        delete[] l;
        r = p = (q = l = new char[N]) + N;
        memcpy(l, s, N);
        eof = fail = 0;
    }
};
rstream rs;
wstream ws;
};  // namespace fastio
#undef SIZ