[C/C++]快速读入读出代码(int, string, double型)

快速读入

1、为什么要有快读

好吧,有些题目看上去十分简单,例如https://www.luogu.com.cn/problem/P4305这道题,实际上数据量巨多,光是一个测试点就可能有几个MB,在这种情况下,就连scanf和printf函数都会超时Σ( ° △ °|||)︴我当初用scanf写时TLE了3个点。我才不会告诉你我是用unordered_map水过去的

所以我们需要找到另外的读入数据的方式。这时就要用到我们平时忽视的一个函数了——getchar()。你肯定会感到惊讶,但是我可以毫不犹豫地告诉你,这玩意确实比上述两个读入方式快许多。我们先一个萝卜一个坑,从读入int型开始慢慢做。

前面主要讲运行原理,如果是需要直接拿代码的,可以调到最后面去查看

2、输入int

有时输入并不会像你想的那么简单,常常会出现一些空格或回车。如果从当前字符直接开始读的话,就会多读入一些空格或者回车,导致数据出错。所以我们需要一个循环先过滤掉前面一些不需要的字符

char ch = ' ';//初始化超重要的 
while(ch < '0' || ch > '9')
{
    ch = getchar();//此处用ch来充当变量 
}

我们似乎要考虑一个情况,如果出现负数,会怎么样?没关系,在这个循环里加入一个判断,来确定是正数还是负数:

char ch = ' ';//初始化超重要的
int w = 1;//是1就是正数 
while(ch < '0' || ch > '9')
{
    if(ch == '-') w = -1;//是-1就是负数 
    ch = getchar();//此处用ch来充当变量 
}

我这段代码有一个隐藏BUG。先让读者观摩几分钟,然后查一查BUG。

BUG就是:如果输入数据时" -  9",它也会判断为负数!不过没关系,很多题目的输入数据已经保证不会出现这种情况。如果还不放心,可以自己改一改(其实是我懒了

目前已经将前面多余的字符过滤掉了,现在要处理的是后面数字部分。此时我们要引用一个新的变量s来存储所输入的数字。处理输入也很简单,不断输入直到不再是1~9之间的数:

while(ch >= '0' && ch <= '9')
{
   s = s * 10 + ch - '0',//读入的数据是字符,需要减去一个ASCLL码 
   ch = getchar();
}

如此,我们最后返回数据就可以了。全代码:

inline int IntRead()//内联函数稍微快一点点 
{
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        s = s * 10 + ch - '0',
        ch = getchar();
    }
    return s * w;
}
int main()
{
    int a;
    a = IntRead();
    cout << a;
}

(等会,我好像忘记打include了)

3、输入string字符串

处理完int输入,字符串输入相对来说简单多了。首先先处理完前面多余的回车与空格,然后再不断读入直到读到回车或空格。这里不再赘述直接上代码:

inline string StringRead()
{
    string str;
    char s = getchar();
    //处理多余回车或空格 
    while (s == ' ' || s == '\n' || s == '\r')
    {
        s = getchar();
    }
    //不断读入直到遇到回车或空格 
    while (s != ' ' && s != '\n' && s != '\r')
    {
        str += s;
        s = getchar();
    }
    return str;
}

4、读入浮点数

终于到一个稍微复杂一点的地方了,读入浮点数。读入浮点数有两个策略,1是先读入字符串然后再进行处理,2是读入的过程中,先读整数部分,然后再读小数部分。这里我选择第二种。

前面处理多余字符和判断负数的方式与int相同,只是后面读入数字过程中要改变一下。我们引入两个变量n、k和m,n存储当前是读整数部分还是小数部分,k表示小数部分的值,m表示小数部分的长度(可省略,但为了方便阅读)。(这样写或许过于复杂?)如果读到小数点,便修改n的值,否则就看n的值来读入整数或小数。

最后返回答案时,直接将k转换为小数,然后加上整数部分,然后再决定是返回负数还是正数。

代码:

inline double DoubleRead()
{
    //double的值可能很大,所以开long long 
    long long s = 0, w = 1, k = 0, n = 0, m = 0;
    char ch = getchar();
    //和int一毛一样有木有 
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') w = -1;
        ch = getchar();
    }
    while((ch >= '0' && ch <= '9') || ch == '.')
    {
        //n = 0代表读入整数,= 1代表读入小数 
        if (ch == '.')
            n = 1;
        else if (n == 0)
            s = s * 10 + ch - '0';
           else k = k * 10 + ch - '0', m++;
        ch = getchar();
    }
    return (pow(0.1, m) * k + s) * w;
}

大致的读入方法就OK了,但是实际上我并没有对代码做优化。

快速读出

有一些题目依旧十分刁钻,输出量巨大,虽然我还没有找到例题,但可以肯定的是绝对有一些不怀好意的出题人会将数据量加大,所以,快速读出代码就闪亮登场了!

1、快速读出int

还是一点一点从0开始做起吧。

这里先放代码

inline void IntWrite (int s)
{
    int k = 0, len = 0;
    if (s == 0) 
        putchar('0');
    while (s)
    {
        k = k * 10 + s % 10;
        s /= 10, len++;
    }
    for (int i = 0;i < len;i++)
    {
        putchar(k % 10 + '0');
        k /= 10;
    }
}

 

先一行行开始讲。如果数字是0,直接将0输出没毛病。我们写的时候会发现一个问题:我们无法得知开头的数字是什么!在不知道数字长度的时候,我们就计算不出开头的数字。那么我们换个角度:从结尾处开始计算。得到数结尾的数字很简单,直接对10取模就可以了。于是我们用一个变量k来存储s翻转过后的值,然后再将k翻转输出。比如将12翻转成21,然后就可以顺利输出。不过,遇到100这个数字,翻转后会成为1!我们就可以引入一个len来保证输出的数不会出错。

2、输出string

输出string就更加简单了!直接上代码:

inline void StringWrite(std::string str)
{
    int i = 0;
    while (str[i] != '\0')
    {
        putchar(str[i]), i++;
    }
}

或许有人会疑惑为什么是以'\0'来判断而不是以.length来获得string长度,因为后者的时间复杂度比前者高(想一想,为什么)。

3、输出double

输出double也不难,先将double转换为long long int型,转换过程中记录小数点在第几位。

inline void DoubleWrite(double a)
{
    int mi = 0, s[100];
    if (a == 0)
        putchar('0');
    while (a != (long long int)a)
    {
        a *= 10, mi++;
    }
    long long int k = a, len = 0;
    while (k != 0)
    {
        s[len] = k % 10, len++, k /= 10;
    }
    for (len -= 1;len >= 0;len--)
    {
        if (len == mi - 1)
            putchar('.');
        putchar(s[len] + '0');
    }
}

这里可能会有人有一些疑问:为什么前面输出int型时不用数组呢?因为double型可以有很多小数,一旦小数数位超过一定阀值就会爆long long int(也就是溢出),所以用数组存。

所有代码

inline int IntRead()
{
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        s = s * 10 + ch - '0',
        ch = getchar();
    }
    return s * w;
}
快速读入int
inline string StringRead()
{
    string str;
    char s = getchar(); 
    while (s == ' ' || s == '\n' || s == '\r')
    {
        s = getchar();
    }
    while (s != ' ' && s != '\n' && s != '\r')
    {
        str += s;
        s = getchar();
    }
    return str;
}
快速读入string
inline double DoubleRead()
{
    long long s = 0, w = 1, k = 0, n = 0, m = 0;
    char ch = getchar(); 
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') w = -1;
        ch = getchar();
    }
    while((ch >= '0' && ch <= '9') || ch == '.')
    {
        if (ch == '.')
            n = 1;
        else if (n == 0)
            s = s * 10 + ch - '0';
           else k = k * 10 + ch - '0', m++;
        ch = getchar();
    }
    return (pow(0.1, m) * k + s) * w;
}
快速读入double
inline void IntWrite(int s)
{
    int k = 0, len = 0;
    if (s == 0) 
        putchar('0');
    while (s)
    {
        k = k * 10 + s % 10;
        s /= 10, len++;
    }
    for (int i = 0;i < len;i++)
    {
        putchar(k % 10 + '0');
        k /= 10;
    }
}
快速读出int
inline void StringWrite(std::string str)
{
    int i = 0;
    while (str[i] != '\0')
    {
        putchar(str[i]), i++;
    }
}
快速读出string
inline void DoubleWrite(double a)
{
    int mi = 0, s[100];
    if (a == 0)
        putchar('0');
    while (a != (long long int)a)
    {
        a *= 10, mi++;
    }
    long long int k = a, len = 0;
    while (k != 0)
    {
        s[len] = k % 10, len++, k /= 10;
    }
    for (len -= 1;len >= 0;len--)
    {
        if (len == mi - 1)
            putchar('.');
        putchar(s[len] + '0');
    }
}
快速读出double

 

最后

这一篇随笔算是比较短的,但是我还是花了一个下午的时间去码代码,但可能依旧有一些小错误。一些代码复制上来的时候我并没有仔细去检查,如果你发现了错误,欢迎在评论区里留言。

你可能感兴趣的:([C/C++]快速读入读出代码(int, string, double型))