[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了,但是實際上我並沒有對代碼做優化。

快速讀出

說起來也奇怪,我的一位信息老師在換行或者只打印單個字符時,特別喜歡用putchar,我起初並無法理解這樣的意思。但是當我成功將快讀寫出來時,我也理解了老師的用意。

有一些題目依舊十分刁鑽,輸出量巨大,雖然我還沒有找到例題,但可以肯定的是絕對有一些不懷好意的出題人會將數據量加大,所以,快速讀出代碼就閃亮登場了!

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

 

最后

這一篇隨筆算是比較短的,但是我還是花了一個下午的時間去碼代碼,但可能依舊有一些小錯誤。一些代碼復制上來的時候我並沒有仔細去檢查,如果你發現了錯誤,歡迎在評論區里留言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM