Talking about the input and output of C/C++

Better reading experience\huge{\color{red}{better reading experience}}better reading experience


0. Stack armor, pass


My level is limited, and my language organization ability is low. I do not guarantee an excellent reading experience, nor do I guarantee that the content is completely accurate. If you have any mistakes or suggestions, please point them out. No wonder .


1. Talk about input and output buffers


1.1 Basic concepts


Don't worry, I know you are in a hurry, but don't worry, so don't worry .

When understanding the input and output buffers, the following basic concepts need to be clarified:

  • I/O stream
  • standard input and output stream
  • file input and output stream

I/O stream


  • The input and output stream is a concept of data transmission.
  • One of the components that make up a computer is I/Oa device, which refers to a device (mouse, keyboard, etc.) used to transfer data from inside the program to an external device (screen, printer, etc.) or from an external device to the inside of the program;
  • The data interaction between the computer and the user is carried out through the I/Odevice, and in order to adapt to the transmission of data between different devices, the concept of input and output streams is proposed.
  • That is, the input and output stream is a unified data input and output protocol, which provides a consistent interface for transferring data between different devices .

standard input and output stream


  • The standard input and output stream refers to the input and output between the program and external devices (such as keyboards and monitors)
  • In Clanguage:
    • In Cthe standard library, the standard input and output streams are stdinand , respectively stdout, as well as the standard error stream stderr.
    • Use the functions and functions <stdio.h>in the header file .scanf()printf()
  • In C++language:
    • In C++the standard library, there is no stdinsuch standard input stream, instead use std::cinand std::outfor standard input and standard output.
    • Use the function <iostream.h>in the header file getline()or >>the and <<operator.
  • In summary, in C++, the use of input and output streams is usually iostreamimplemented through the library, while in , it is implemented Cthrough the library.stdio

file input and output stream


  • The file input and output stream saves data in a file on the disk. By opening and closing the file, the program can use the file input and output stream to read and write data.
  • In Clanguage:
    • File input and output streams are implemented using Cfile pointers in the standard library FILE*.
    • Operation functions include fopen(), fclose(), fread(), fwrite()and so on.
  • In C++language:
    • The file input and output stream is Cencapsulated based on the file operation functions in the standard library, that is, fstreamthe class.
    • Specifically, implemented by std::ifstreamand classes, which are derived classes of and classes.std::ofstreamstd::istreamstd::ostream

Compared with the standard input and output stream, the file input and output stream needs to explicitly specify the file to be read and written , so it is more cumbersome to use, but it is also more flexible: the file input and output stream can handle any type of file, including text files and binary files , while standard input and output streams can only handle character streams. In addition, the file input and output stream can read and write files through random access to the file, while the standard input and output stream can only read and write sequentially.


1.2 Input and output buffer


What is an input and output buffer?


As the name implies, the input and output buffer is the area of ​​the input and output buffer .

In C/C++, the input and output buffer is a temporary storage area used to store input and output data :

  • An input buffer is a temporary storage area where input data is stored prior to data stream input.
  • An output buffer is a temporary storage area where output data is stored until the data stream is output.

In human terms: The input and output buffer is a piece of memory temporarily opened up to save these input and output streams .


Why set the input and output buffers?


Zhongsuo porridge juice, because it is needed, so set :

  • Buffers are in memory, while peripherals are in hardware.
  • Reading and writing data from memory is faster than reading and writing data from hardware.

Therefore, when a program needs to read or write a large amount of data, the buffer can be used to store the data in memory first, and then write or read at one time, avoiding the overhead of frequent access to hardware. In addition, buffers can optimize the arrangement and formatting of data so that it can be read and written more efficiently.

In human terms: The existence of the buffer is to improve the efficiency of input and output and reduce the number of accesses to peripherals .


How are input and output buffers different in C/C++?


don't rush don't rush


First of all, don't worry, and second, don't worry , so let's first understand: **What is the space for the input and output buffer allocated? Where is the development? When will it be opened? **this problem:

  • The space of the input and output buffer is usually allocated by the operating system ;
  • In general, it is allocated from the memory when the program is running, allocated in the program running space , not in the kernel space of the operating system;
  • The timing of the allocation and the size of the allocated space will vary according to the specific implementation . Generally , when the program writes or reads data to the buffer through the input and output functions, the buffer will be allocated.

Specific location :

  • When to allocate a buffer:

    • For standard input and output streams: The buffer space is usually pre-allocated when the program starts.
    • For file input and output streams: the buffer space is dynamically allocated when the file stream and stream buffer objects are created, and these objects are usually initialized at the beginning of the program.
    • The size of the buffer is usually an implementation detail, but in general, the size of the buffer should be large enough to hold the normal size of the input or output data, but not so large that it wastes memory.
  • The size of the allocated buffer:

    • The size of the buffer should be large enough to hold the normal size of input or output data, but not so large that memory is wasted .

    • The allocation of the buffer size is done by the implementing library, and the specific implementation details may vary depending on the compiler or operating system.

    • Generally speaking, the implementation library will allocate the buffer space by calling the system call provided by the operating system or the dynamic memory allocation function .

    • In the case of tight memory space, the size of the buffer may be limited, which may affect the performance and reliability of the program.


hurry hurry hurry hurry hurry


I know you're in a hurry, but don't worry , this part actually doesn't need to be too tangled. of. Right:

  • In Cthe language, the standard input and output library <stdio.h>provides the implementation of the input and output buffer.
    • Three functions are mainly used: setbuf(), setvbuf(), fflush().
    • Among them, setbuf()and setvbuf()can be used to set the buffer, and fflush()used to clear the buffer and output the data in the buffer to the file.
    • Therefore, Cthe input and output functions in, such as scanf()and printf()etc., are not type-safe :
      • They rely on format strings to indicate the type of input/output data.
      • Incorrect format strings can lead to unpredictable results such as buffer overflows and undefined behavior.
  • In C++, <iostream>the library provides the implementation of input and output buffers.
    • Two different buffers are provided: streambufand filebuf.
    • streambufis <iostream>the base class of the library, which provides access to the input and output buffers; and filebufis <fstream>the base class of the library, which provides access to the file input and output buffers.
    • However, <iostream>the library also provides some functions like setbuf(), setvbuf(), flush()etc. to manage the input and output buffers. After closing the synchronous stream, <iostream>the library uses a mechanism different from the standard input and output library to improve efficiency, such as using string streams stringstreamand buffered streams buffer stream.
    • Therefore, C++the input and output functions in, such as std::cinand std::coutetc., are type-safe :
      • They use type-safe C++stream semantics, where data types are determined statically rather than dynamically.
      • This means that the data type is determined at compile time, rather than being dynamically determined at runtime from the format string.
      • This kind of static type checking can detect type mismatch errors at compile time, making the C++input and output of the more type-safe.

This is why, you can still C++use scanf()and in printf(), but it is still recommended C++to use <iostream>the standard input and output provided by the library in , and why we often say C++that Cis more suitable for object-oriented than .

Summary: This part really doesn't have to be too tangled. Fair. correct. sane. Hit the nail on the head. real.


2. Talk about the way of input and output


2.1 Input and output of C/C++


You are in a hurry, you are in a hurry, you are in a hurry, because you are confused , you don't understand stdin, scanf, cin, std::cin, getline, stringstreamand stdout, printf, cout, std::coutwhat are these things , right? Let's make it clear:

  • stdinis Cthe standard input stream in the language.
  • cinis C++the standard input stream in , and std::cinis C++the standard input stream in the standard library namespace, which is an abbreviation cinfor using the namespace , which is an alias for .stdcinstd::cin
  • scanf()is Can input function in the language, and cinand std::cinare C++input streams in . scanf()The parameters of need to use a format string to specify the type of input data, and cinand std::cincan automatically identify the type of input data.
  • getline()It is C++an input function in , which can be used to read a line of text data from the input stream, and the delimiter can be specified. getline()Can be used instead of scanf()and cinfor reading string type data.
  • stdoutis Cthe standard output stream in the language.
  • coutis C++the standard output stream in and std::coutis C++the standard output stream in the standard library namespace. The difference between them is the same cinas and std::cin.
  • printf()is Can output function in the language, and coutand std::coutare C++output streams in the . printf()The parameters of need to use a format string to specify the type of output data, and coutand std::coutcan automatically identify the type of output data.
  • As for stringstreamthis crazy thing, let's put it in the end.

scanf() and printf()


Because we are so familiar with these two things, we are not unfamiliar with these two things at all . These two are Cthe standard input and standard output functions in the language.

For that printf(), just pay attention to the following points:

  • usage:scanf(format, argument_list);
  • It is used to output data to the console, and can output various types of data, such as integers, floating point numbers, characters, strings, etc.
  • When outputting a string, you need to pay attention to whether the string contains special characters, such as newline characters, tab characters, etc., and you need to use corresponding escape characters to represent them.
  • Formatted output can be used to control the format of the output , such as output precision, alignment, etc.

And for scanf(), in addition to the basic points of attention:

  • usage:scanf(format, argument_list);
  • It is used to input data from the console, and can read various types of data, such as integers, floating point numbers, characters, strings, etc.
  • scanf()When inputting data, it is required that the data format formatmatches the format specified in the string, otherwise an error will be generated.

Also need to pay attention: the buffer scanf()of the function will not be cleared automatically , so you need to use fflush(stdin)the statement to clear the buffer to prevent the input data from being received by the next input function. If you just want to deal with the newline character, \nyou can use getchar()read to replace the newline character " eat up".

Take a chestnut :

Observe the following code:

#include <stdio.h>

int main(){
    
    
    int n;                  //声明 int 类型变量 n
    scanf("%d", &n);        //读入 int 类型变量 n
    printf("%d\n", n);      //输出 int 类型变量 n 并且换行
    char c = getchar();     //读入一个字符,并存储在 char 类型变量 c 中
    printf("%c", c);        //输出 char 类型变量 c
    printf("14\n");         //输出 14 并且换行
    return 0;
}

Suppose you run it and enter the following in the console:

114
5

In theory, I'd expect to get the output:

114
514

But in fact, the console hums and hums and hums and hums, and outputs the following:

114

14

5Even the console did not receive the character you entered later .

In this example, scanf("%d", &n)a number from the input stream is read 114and stored in a variable n. However, since there is a newline character in the input buffer \n, getchar()the function will read this newline character and store it in the variable c, resulting in such a result. The data in the buffer is not automatically cleared , which is why the console simply ignores your subsequent input and outputs unexpected content.

Then continue to observe the following code:

#include <stdio.h>

int main(){
    
    
    int n;                  //声明 int 类型变量 n
    scanf("%d", &n);        //读入 int 类型变量 n
    printf("%d\n", n);      //输出 int 类型变量 n 并且换行
    getchar();              //用 getchar() 吃掉缓冲区中的 '\n'
    char c = getchar();     //读入一个字符,并存储在 char 类型变量 c 中
    printf("%c", c);        //输出 char 类型变量 c
    printf("14\n");         //输出 14 并且换行
    return 0;
}

Recompile and run and enter the following in the console:

114
5

You can find that the console is humming and ah ah ah ah ah output:

114
514

In this example, in order to avoid the situation that the above buffer is not cleared, we manually clear the input buffer after reading the data, and use to getchar()read the newline character in the buffer \n, so that the subsequent characters 5are successfully read in, and the final output expected content.


cin and cout


cinand coutare C++input and output streams, which can be used to implement console input and output operations. Generally, when using cinand , coutyou can simplify the code by introducing using namespace std;, but you can also use fully qualified names std::cinand std::cout.

Since the input cinand coutoutput of and will automatically match the corresponding data types, the formatted input and output for the two is not the focus of the discussion here, but here, we need to mention its about **synchronized stream** the concept of:

  • Synchronous stream means that when data is output in the program stream, the program must wait until the data is completely output to the device before continuing to execute the following code.
  • Likewise, when a program tries to read data from an input device, the program waits for the user to enter complete data before continuing to execute the following code.

Although synchronous flow can ensure the correctness of input and output, it will affect the efficiency of the program in some scenarios, especially in the case of a large amount of data input and output.

That's why, even if C++would rather give up the high performance of scanf()and printf(), but also get the safety and correctness brought by the synchronization of input and output streams, which also makes is C++more suitable for object-oriented development.

Note :

  • scanf()and printf()also have a synchronous stream mechanism, but their buffer implementation is more low-level and more efficient.
  • In addition, the type checking mechanism of cinand coutand various other operations are also one of the factors that affect its performance.

getchar() and getline()


The two are put together simply because they look alike , but they are vastly different:

  • getchar()The function reads a character from standard input (stdin) and returns the ASCIIcode value of that character.

  • It is usually used to read a single character or an array of characters, and can implement simple input operations.

  • When using it, it should be noted that since the input characters are directly input through the keyboard, it is necessary to press the Enter key to send the input characters into the buffer, and only then can the input content be read getchar().

  • getline()The function reads a line of text from the input stream and stores it into a string object, and can read a whole line of input including spaces.

  • When using it, it should be noted that if the default delimiter is used \n, getline()the newline character will be read into the buffer. If the read input is used next time getline(), the newline character in the buffer will be read instead of the expected input . At this time, you can cin.ignore()clear the characters in the buffer by calling, or specify other separators.

The issue getchar()of the buffer zone has already been mentioned, here is a getline()chestnut of :

Observe the following code:

#include <iostream>
#include <string>

using namespace std;

int main() {
    
    

    string s; 

    getline(cin, s);  //读入 string 类型 s

    cout << "First: " << s << endl;  //输出 s

    getline(cin, s);  //在此读入

    cout << "Second: " << s << endl;  //再次输出 s

    return 0;
}

Suppose you run it and enter the following in the console:

114
514

In theory, I'd expect to get the output:

First: 114
Second: 514

But in fact, the console hums and hums and hums and hums, and outputs the following:

First: 114
Second: 514

You'll be amazed that expectations are met, and you think, "Hey, this isn't faultless crap L ys LysL ys playing with me? "

Don't be in a hurry, let me be in a hurry.

getline()There are actually three parameters, the third parameter is the delimiter parameter, that is, the getline()data will be divided and processed by this parameter, and by default, it getline()will be used \nas the delimiter, that is, we use by default getline(cin, s, '\n');.

So in this example, 114pressing the Enter key after entering is considered a delimiter and is removed from the input stream, while \nstill remaining in the buffer. The second getline()call then reads the remaining characters in the buffer, i.e. removes and stores "\n514"the s in it . So the output is as expected.\n514

Let's re-specify getline()the delimiter and modify the following code:

#include <iostream>
#include <string>

using namespace std;

int main() {
    
    

    string s; 

    getline(cin, s, ',');  //读入 string 类型 s,并以 ',' 为分隔符

    cout << "First: " << s << endl;  //输出 s

    getline(cin, s, ',');  //在此读入

    cout << "Second: " << s << endl;  //再次输出 s,并以 ',' 为分隔符

    return 0;
}

Suppose you run it and enter the following in the console:

114,
514,

In theory, I'd expect to get the output:

First: 114
Second: 514

But in fact, the console hums and hums and hums and hums, and outputs the following:

First: 114
Second: 
514

You'll be surprised that this time it doesn't live up to expectations, and you think, "Eh, this isn't crap, rubbish L ys LysL ys playing with me? "

You are in a hurry, but don't be in a hurry.

In this example, 114,pressing the Enter key after entering ','is considered a delimiter and is removed from the input stream, but subsequent entered \nremains in the buffer. The second getline()call then reads the remaining characters in the buffer, i.e. removes and stores "\n514,"the s in it . So the output is not as expected.','\n514

In order to avoid this result, we also need to manually clear the buffer, we can use getchar()the "eat" buffer \n, but it is recommended to use the following method:

#include <iostream>
#include <string>

using namespace std;

int main() {
    
    

    string s; 

    getline(cin, s, ',');  //读入 string 类型 s

    cout << "First: " << s << endl;  //输出 s

    // 使用 cin.ignore() 忽略掉输入缓冲区中的换行符
    // 也可以使用 cin.get() 读取缓冲区中的换行符
    cin.ignore();
    // cin.get();

    getline(cin, s, ',');  //在此读入

    cout << "Second: " << s << endl;  //再次输出 s

    return 0;
}

Finally got the expected result.

First: 114
Second: 514

Generally speaking, getchar()it is suitable for reading a single character or an array of characters, and getline()it is suitable for reading a whole line of text. When using the two, you need to pay attention to different input methods and buffer processing .


stringstream


  • stringstreamIt is C++a data stream object provided by the standard library, which is used to perform input and output operations on strings in memory.
  • It can perform input and output like cinand cout, and has interfaces and methods similar to input and output streams, such as <<and >>operators.
  • It provides a method to convert a string into a data type, which is convenient for programmers to process data.
  • In is also C++type safe .stringstream

stringstreamThe input and output streams such as and cin, couthave similar interfaces and methods, and can perform input and output operations, but their scopes are different. cin, coutand other input and output streams are usually used for standard input and output streams, and stringstreamare usually used for string processing.

Usually we can use to stringstreamsplit, convert, concatenate and other operations on the string, and then use cinor coutto output to the standard input and output stream:

  • We can use getline()the function to read a line of string from standard input;
  • Then use stringstreamto convert it to a numeric type, and finally use coutto output to the standard output stream.
  • Such codes can not only handle the standard input and output streams, but also conveniently perform string operations, which improves the scalability and reusability of the program.

Take a chestnut :

Observe the following code:

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main() {
    
    
    stringstream s;
    string name = "Lys";
    int age = 13;
    double height = 1.86;
    string status = "is a dog";

    s << "Name: " << name << ", Age: " << age << ", Height: " << height << ", Status: " << status;
    string str = s.str();

    cout << str << endl;

    return 0;
}

In this example, we first create a stringstreamobject s, then use <<operators to insert strings, integers and floats, and a string into the s, and finally use str()the method to convert all inserted data into a string and print it to standard output.

For another example, observe the following code:

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main() {
    
    
    
    string s;
    getline(cin, s);

    stringstream ss(s);
    string str;
    
    while(ss >> str){
    
    
        cout << str << endl;
    }

    return 0;
}

Compile and run and enter the following in the console:

Lys is a dog.

Then get the following output:

Lys
is
a
dog.

In this example, we first create a stringtype s, and use getline(cin, s)to read in the string, and then sconvert the string into stringstreaman object ss, and then use the object to filter spaces and assign values ​​to it str, and finally print it to the standard output.


2.2 Turn off C++ standard stream synchronization


As mentioned earlier, due to cinand coutThere are functions such as synchronous flow mechanism and type checking mechanism that affect its performance. Therefore, in the face of a scene that requires a large number of input and output, the input scanf()and printf()output efficiency of and is significantly better than cinand cout, but we can still turn off the synchronous flow by setting the synchronous flow flag of and to improve the efficiency of the program, even better than and .cincoutscanf()printf()

In C++the program, add the following statement to optimize the input and output flow speed and interactivity:

 ios::sync_with_stdio(false);
 cin.tie(nullptr);
 cout.tie(nullptr);
  • ios::sync_with_stdio(false): Turn off the synchronization C++of the standard input and output stream with Cthe language input and output stream, thus speeding up the input and output.
  • cin.tie(nullptr): Unbind cinand cout, so as to avoid the problem that the output buffer is refreshed every time when the input is read.
  • cout.tie(nullptr): coutis bound by default nullptr, in fact this sentence does not need to be added . See Ok, lets talk about cout.tie once and forever for a related discussion .

It should be noted that after the synchronization of the input and output streams is turned off, the input and output functions of the language cannot be C++used in the code C, otherwise it may cause problems such as incomplete output or wrong output order. In addition, after unbinding, you need to manually refresh the output buffer , otherwise the output content may be incomplete or not timely. Therefore, when using these statements, you need to carefully consider the usage scenarios and execution order to avoid unexpected errors.

The following statement:

 ios::sync_with_stdio(false);
 cin.tie(0);
 cout.tie(0);

The purpose of increasing the input and output speed can also be achieved. This is more general than using nullptr, as it may not be supported in some older C++compilers nullptr.

Generally speaking, there is not much difference between these two writing methods, except that the null pointer constant used when unbinding is different, but both can achieve the effect of improving the input and output speed.


3. Final exercise


3.1 Pan-Caesar encryption


Original Link

Description :

As we all know, in network security, it is divided into plaintext and ciphertext. Caesar encryption is to move all the English letters in a plaintext back three digits ( ZZThe next digit of Z is AAA ), likeaaA moves backward three digits to becomeddd A A A moves backward three digits to becomeDDD Z Z Z moves backward three digits to becomeCCC , but Pan-Caesar encryption is not so simple. It moves each letter in the plaintext backward by k bits to obtain the ciphertext, and one will be appended at the end of the ciphertext. This question wants you to solve it by getting the?ciphertext the original plaintext.

Input format :

In the first line, enter a positive integer kkk indicates the number of digits the letter moves backward.

Next, enter several lines of character strings to represent the ciphertext, and the data input ensures that only the last character of the ciphertext is ?.

Output format :

Output the original plaintext.

Data range :

0 ≤ k ≤ 100 0 \le k \le 1000k100

Sample input :

2
*eee/peee++?

Sample output :

*ccc/nccc++

3.2 Solution


You are already a mature  ACMer \text{ ACMer } As an ACMer  , you must learn to analyze and solve problems by yourself. If you can't solve it, just solve it yourself.

#include <iostream>
#include <cstring>

using namespace std;

void solve(){
    
    
    int k; cin >> k;
    string s;
    k %= 26;
    getchar();  //清空缓冲区中的 '\n'
    while(getline(cin, s)){
    
    
        for(int i = 0; i < s.size(); i ++){
    
    
            char st = s[i];
            if(st >= 'a' && st <= 'z') cout << char(st - k < 'a' ? st - k + 26 : st - k);
            else if(st >= 'A' && st <= 'Z') cout << char(st - k < 'A' ? st - k + 26 : st - k);
            else if(st == '?') break;
            else cout << st;
        }
        cout << endl;
    }
}

int main(){
    
    
    solve();
    return 0;
}

Guess you like

Origin blog.csdn.net/LYS00Q/article/details/129233151