[C++] IO stream in C++

Article directory

  • 1. Input and output of C language
  • 2. What is flow?
  • 3. C++ IO stream
    • 1.C++ standard IO stream
    • 2.C++ file IO stream
  • 4. A brief introduction to stringstream

1. Input and output of C language

The most frequently used input and output methods in C language are scanf () and printf ():

scanf(): Read data from the standard input device (keyboard) and store the value in the memory corresponding to the variable.

printf(): Output the specified text/string to the standard output device (screen). Pay attention to the width output and precision output controls.

C language uses corresponding buffers for input and output. As shown below:

Understanding of input and output buffers:

1. You can shield the implementation of low-level I/O. The implementation of low-level I/O depends on the implementation of the kernel of the operating system itself, so if you can shield this part of the difference, you can easily write a portable program.

2. You can use this part of the content to implement the behavior of “row” reading. For computers, there is no concept of “row”. With this part, the concept of “row” can be defined. The contents of the buffer are then parsed, returning a “line”.

Note: In addition to the standard input and output interfaces scanf and printf, the C language also has the file operation read and write interface fread/fwrite/fscanf/fprintf, and the string serialization and deserialization interface sprintf/snprintf/sscanf.

2. What is stream

“Flow” means flow, which is the process of material flowing from one place to another. It is an abstract description of an orderly, continuous and directional data (the unit can be bit, byte, packet). Its characteristics are: orderly and continuous, directional

C++ stream refers to the process of inputting information from an external input device (such as a keyboard) to the inside of a computer (such as a memory) and outputting information from the memory to an external output device (a monitor). This input-output process is vividly compared to “flow”.

In order to realize this flow, C++ defines an I/O standard class library. Each of these classes is called a stream/stream class to complete a certain function.

3. C++ IO stream

The C++ system implements a huge I/O standard class library, of which ios is the base class, and other classes are directly or indirectly derived from the ios class.

1.C++ standard IO stream

The C++ standard library provides four global stream objects cin, cout, cerr, and clog:

  • cin: perform standard input, that is, data is entered into the program through the keyboard
  • cout: Perform standard output, that is, data flows from memory to the console (monitor)
  • cerr: used for standard error output
  • clog: Output logs

It can be seen from the inheritance system diagram of the I/O standard class library that cout, cerr, and clog are three different objects of the ostream class. Therefore, there is basically no difference between these three objects now, but the application scenarios are different.

Note: When using the global object stream, you must include the file (iostream) and introduce the std standard namespace.

Notes on using cin/cout:

1.cin is a buffer stream. The data entered by the keyboard is stored in the buffer. When it is to be extracted, it is taken from the buffer. If you input too much at one time, it will be left there and used slowly. If you input incorrectly, you must modify it before pressing the Enter key. If you press the Enter key, it will be irreversible**. **Only after the data in the input buffer is fetched, new data is required to be input.

2. The input data type must be consistent with the data type to be extracted, otherwise an error will occur. If an error occurs, the corresponding bit in the state word state of the stream is set (set to 1), and the program continues.

3. Both spaces and carriage returns can be used as separators between data, so multiple data can be entered on one line or in separate lines. But if it is a character type and a string, spaces (ASCII code is 32) cannot be input with cin, and there cannot be spaces in the string. The carriage return character cannot be read either.

4.cin and cout can directly input and output built-in type data. The reason: the standard library has overloaded all built-in type input and output:

5. For custom types, if you want to support the standard input and output of cin and cout, you need to overload << and >>. It should be noted that when a custom type overloads the stream insertion and stream extraction operators, it is necessary to first declare operator<< and operator>> as friend functions of the class, and then define the function outside the class.

Below we give an example of date input and output overloading:

class Date
{<!-- -->
public:

//The input and output functions are declared as friends of the class
friend ostream & amp; operator<<(ostream & amp; out, const Date & amp; d);
friend istream & amp; operator>>(istream & amp; in, Date & amp; d);
Date(int year=1,int month=1,int day=1)
:_year(year)
,_month(month)
,_day(day)
{<!-- -->}

private:
int _year = 1;
int _month = 1;
int _day = 1;
};

// Implemented outside the class
ostream & amp; operator<<(ostream & amp; out, const Date & amp; d)
{<!-- -->
out << d._year << " " << d._month << " " << d._day << endl;
return out;
}

istream & amp; operator>>(istream & amp; in, Date & amp; d)
{<!-- -->
in >> d._year >> d._month >> d._day;
return in;
}

6. Input and output in online OJ:

  • For IO type algorithms, loop input is generally required:
  • Output: Strictly follow the requirements of the topic, no more or less spaces will do.
  • When typing continuously, it ends when ctrl + Z is entered under the vs series compiler.
//Single element loop input
while (cin >> a)
{<!-- -->
// ...
}
//Multiple elements loop input
while (c >> a >> b >> c)
{<!-- -->
// ...
}
//Receive the entire line
while (cin >> str)
{<!-- -->
// ...
}

We can see that the above code contains the string type, but we also find that the return value of the overloaded stream extraction operator operator>> in the string class is an object of istream type.

However, how can istream, as a custom type object, be used as a condition for logical judgment?

We know that generally only three data types can be used as logical judgments:

1. Shaping: non-0 represents true, 0 represents false

2. Pointer type: non-null pointer represents true, null pointer represents false

3.bool type: true represents true, false represents false

Back to the above question, how do istream type objects do this? The answer is that bool is overloaded internally in the istream class:

Since the stream extraction operator is overloaded in the istream class, when we use while(cin>>str) to extract data from the stream, operator>> is called. This function returns an istream type object; at the same time, because the istream class also Bool is overloaded, and any istream object will call the operator bool function. When the operator bool function is called, if the reception of the stream fails or there is an end flag, it will return false, otherwise it will return true, so the object of the custom type istream can be as a logical judgment condition

We noticed that the explict keyword was added in front of the operator bool function to prevent implicit type conversion from converting a class object into a bool type, thereby causing misjudgment in conditional judgment.

In addition, we can also overload operator >> and operator bool in custom types, so that they can support logical judgment:

class Date
{<!-- -->
friend ostream & amp; operator << (ostream & amp; out, const Date & amp; d);
friend istream & amp; operator >> (istream & amp; in, Date & amp; d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{<!-- -->}

operator bool()
{<!-- -->
// This is written randomly, assuming the input _year is 0, then it ends
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};

istream & amp; operator >> (istream & amp; in, Date & amp; d)
{<!-- -->
in >> d._year >> d._month >> d._day;
return in;
}

ostream & amp; operator << (ostream & amp; out, const Date & amp; d)
{<!-- -->
out << d._year << " " << d._month << " " << d._day;
return out;
}
int main()
{<!-- -->
// Automatically identify the essence of types--function overloading
// Built-in types can be used directly - because the ostream type has been implemented in the library
int i = 1;
double j = 2.2;
cout << i << endl;
cout << j << endl;

// Custom types require us to overload << and >> ourselves
Date d(2023, 7, 15);
cout << d;
while (d)
{<!-- -->
cin >> d;
cout << d;
}

return 0;
}

c Additionally, we look at the way of overloading operator bool in C++98 as overloading void*

This is because overloading bool class objects does not necessarily have to be overloaded as bool type, it can also be overloaded as integer, or pointer type, because they can all be judged as logic. The essence of operator bool is to convert a custom type into a built-in type by overloading it. Its use is not limited to logical condition judgment, but can also be used in other places, such as the following example:

class A
{<!-- -->
public:
A(int a1, int a2)
:_a1(a1)
,_a2(a2)
{<!-- -->}

operator int()
{<!-- -->
return _a1 + _a2;
}

private:
int _a1;
int _a2;
};

int main()
{<!-- -->
A a(1, 2);
int n = a;
cout << n << endl;
return 0;
}

Supplement: Class context conversion

In C++, contextual conversion of a class refers to converting an object of one type into an object of another type in a specific context. There are several types of context conversions in C++, including implicit conversions and explicit conversions.

  1. Implicit Conversion: Implicit conversion refers to automatic type conversion without explicitly specifying a conversion operator. For example:

    • Implicit conversion between numeric types: You can convert a smaller numeric type to a larger numeric type, such as converting the int type to the long type.
    • Inheritance relationship conversion between types: A pointer or reference of a derived class can be implicitly converted to a pointer or reference of a base class.
    • Implicit conversion of literals: Literal constants can be converted to objects of a specific type.
  2. Explicit Conversion: Explicit conversion refers to explicitly converting an object of one type to an object of another type by using a conversion operator. For example:

    • C-style type conversion: Use the cast operator for type conversion, such as (int)floatNumber.
    • Type conversion functions in C++: Use conversion functions such as static_cast, dynamic_cast, const_cast, reinterpret_cast to perform type conversion Convert.

Note that explicit conversion should be used with caution, as it can bypass the compiler’s type checking, potentially leading to type mismatches or runtime errors. When performing type conversions, you should ensure that the conversion is safe and follow good programming practices.

2.C++ file IO stream

There are three classes for file operations in C++, namely ifstream/ofstream/fstream, as follows:

  • ifstream – input file stream, used for input only
  • ofstream – output file stream, used for output only
  • fstream – file stream for input and output

The relationship between these three classes is as follows:

Below we introduce the use of the fstream class in file operations:

Constructor

fstream supports no-parameter construction and parameterized construction, does not support copy construction, and supports move construction:

  • No-argument construction: Create a stream object that is not associated with any file, that is, no file will be opened. When using the default constructor to create an fstream object, we need to use the open function to associate an object with a file.

  • Construction with parameters: You need to specify the file name to be opened and the way to open the file. If you want to open it in multiple ways, you need to use the | operator. The way to open a file in C++ is as follows: in/out means that the object performs read/write operations on the file, and binary/ate/app/trunc is divided into formats that represent reading/writing data to the file – binary read or write /Write to end of file/Append write/Clear the contents of the file before writing

By default, sfstream opens the file in read-only form, reads and writes data to the file in text format. At the same time, the file object will automatically call the close member function to close the file when it is destructed. We can also do it automatically and manually. close file

File operation related member functions:

We can use the stream extraction and stream insertion operators to read and write files, or we can use member functions to read and write:

However, the most commonly used interfaces in actual development are operator>> and operator<<. This is because these two interfaces are very convenient to use. Reading and writing data to files through these two interfaces is the same as reading data from standard input and output.

Here is an example of object-oriented file manipulation in C++:

class Date
{<!-- -->
friend ostream & amp; operator << (ostream & amp; out, const Date & amp; d);
friend istream & amp; operator >> (istream & amp; in, Date & amp; d);
public:
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{<!-- -->}

operator bool()
{<!-- -->
// This is written randomly, assuming the input _year is 0, then it ends
if (_year == 0)
return false;
else
return true;
}
private:
int _year;
int _month;
int _day;
};

istream & amp; operator >> (istream & amp; in, Date & amp; d)
{<!-- -->
in >> d._year >> d._month >> d._day;
return in;
}

ostream & amp; operator << (ostream & amp; out, const Date & amp; d)
{<!-- -->
out << d._year << " " << d._month << " " << d._day;
return out;
}

struct ServerInfo
{<!-- -->
char _address[32];
int _port;
Date_date;
};

struct ConfigManager
{<!-- -->
public:
ConfigManager(const char* filename)
:_filename(filename)
{<!-- -->}
void WriteBin(const ServerInfo & amp; info)
{<!-- -->
ofstream ofs(_filename, ios_base::out | ios_base::binary);
ofs.write((const char*) & amp;info, sizeof(info));
}
void ReadBin(ServerInfo & amp; info)
{<!-- -->
ifstream ifs(_filename, ios_base::in | ios_base::binary);
ifs.read((char*) & amp;info, sizeof(info));
}
// The advantage of C++ file stream is that it can be used for both built-in types and custom types.
// In the same way, go to stream insertion and stream extraction of data
// Of course, the custom type Date here needs to overload >> and <<
// istream & amp; operator >> (istream & amp; in, Date & amp; d)
// ostream & amp; operator << (ostream & amp; out, const Date & amp; d)
void WriteText(const ServerInfo & amp; info)
{<!-- -->
ofstream ofs(_filename);
ofs << info._address << " " << info._port << " " << info._date;
}
void ReadText(ServerInfo & amp; info)
{<!-- -->
ifstream ifs(_filename);
ifs >> info._address >> info._port >> info._date;
}
private:
string _filename; // Configuration file
};
int main()
{<!-- -->
ServerInfo winfo = {<!-- --> "192.0.0.1", 80, {<!-- --> 2023, 7, 15 } };

//Binary reading and writing
ConfigManager cf_bin("test.bin");
cf_bin.WriteBin(winfo);
ServerInfo rbinfo;
cf_bin.ReadBin(rbinfo);
cout << rbinfo._address << " " << rbinfo._port << " "<< rbinfo._date << endl;
//Text reading and writing
ConfigManager cf_text("test.text");
cf_text.WriteText(winfo);
ServerInfo rtinfo;
cf_text.ReadText(rtinfo);
cout << rtinfo._address << " " << rtinfo._port << " " <<rtinfo._date << endl;
return 0;
}

Note: If the file is opened in binary format, you cannot write string objects directly to the file, because string is a custom type, in which in addition to the char* _str character array, there are many other member variables and member functions. , if it is written directly to the file, then the data written is the memory space occupied by the object, that is, the addresses of the member variables and member functions in the object, not the content of the string itself, that is, the _str variable points to Character array in heap space. In this way, when reading the file, the original string data cannot be correctly obtained (the object may have been destroyed the next time the data is read, and then accessing the character array on the heap through the address of the member variable in the old object will be causing wild pointer access)

However, a file opened in text format can directly write string objects to it, because in a text file, the data is stored in the form of character encoding, that is, a single character is converted into the corresponding digital form and then stored in the file, that is, the heap The ASCII value corresponding to the character array in the space is written to the file. Therefore, the string array can be written directly to the file as text and read directly through operations such as the >> operator

4. A brief introduction to stringstream

In C language, if you want to convert the data of an integer variable into string format, one is to use itoa() function, and the other is to use sprintf() function

However, when converting both functions, they need to provide space to save the results first. How much space should be given is difficult to define, and when the conversion format does not match, it may You can also get incorrect results or even program crashs.

In C++, you can use stringstream class objects to get around this problem. If you want to use stringstream in your program, you must include the header file. Under this header file, there are three classes in the standard library: istringstream, ostringstream and stringstream, which are used to perform stream input, output and input and output operations respectively. This article mainly introduces stringstream

stringstream can be mainly used for:

1. Format numeric type data into string

#include<sstream>
int main()
{<!-- -->
int a = 12345678;
string sa;
// Convert an integer variable into a string and store it in a string class object
stringstreams;
s << a;
s >> sa;
cout << sa << endl;
// clear()
// Note that when converting multiple times, you must use clear to clear the last conversion status.
// stringstreams will set its internal state to badbit at the end of the conversion (that is, after the last conversion)
// Therefore, the next conversion must call clear() to reset the state to goodbit before it can be converted.
// But clear() will not clear the underlying string of stringstreams

// s.str("");
    // Set the stringstream underlying management string object to "",
    // Otherwise, during multiple conversions, all results will be accumulated in the underlying string object.

s.str("");
s.clear(); // Clear s, otherwise the conversion will fail.
double d = 12.34;
s << d;
s >> sa;
string sValue;
sValue = s.str(); // str() method: returns the string type managed in stringsteam
cout << sValue << endl;
return 0;
}

2. String concatenation

int main()
{<!-- -->
stringstream sstream;
// Put multiple strings into sstream
sstream << "first" << " " << "string,";
sstream << "second string";
cout << "strResult is: " << sstream.str() << endl;
// clear sstream
sstream.str("");
sstream << "third string";
cout << "After clear, strResult is: " << sstream.str() << endl;
return 0;
}

3. Serialize and deserialize structured data

struct ChatInfo
{<!-- -->
string _name; // name
int _id; //id
Date _date; //time
string _msg; //Chat information
};
int main()
{<!-- -->
// Serialize structure information into string
ChatInfo winfo = {<!-- --> "Zhang San", 135246, {<!-- --> 2023, 7, 15 }, "Let's watch a movie together in the evening"
};
ostringstream oss;
oss << winfo._name << " " << winfo._id << " " << winfo._date << " "
<< winfo._msg;
string str = oss.str();
cout << str << endl << endl;
// We send this string to the object through the network. In actual development, the information is relatively more complicated.
// Generally, Json, xml and other methods will be used for better support.
//The string is parsed into structural information
ChatInfo rInfo;
isstringstream iss(str);
iss >> rInfo._name >> rInfo._id >> rInfo._date >> rInfo._msg;
cout << "------------------------------------------------- ---------"
<< endl;
cout << "Name:" << rInfo._name << "(" << rInfo._id << ") ";
cout << rInfo._date << endl;
cout << rInfo._name << ":>" << rInfo._msg << endl;
cout << "------------------------------------------------- ---------"
<< endl;
return 0;
}

have to be aware of is:

1. Serialization and deserialization operations are very important in the network and are used to transfer data between different computers.

2. When the client needs to send a request to the server, it needs to serialize the request data into a byte stream and transmit it to the server through the network. After the server receives the request, it needs to deserialize the received byte stream. ization operation to obtain the original request data, so as to obtain the specific information requested by the client and respond accordingly.

3. Although the stringstream provided by C++ can complete serialization and deserialization operations, it is only suitable for serialization and deserialization operations of small objects. If you need to process a large amount of data or the data requires high-performance serialization And deserialization operations require more professional serialization libraries, such as Json and protobuf.

stringstream note:

1. Stringstream actually maintains a string type object at its bottom layer to save the results.

2. When converting multiple data types, you must use clear() to clear it to achieve correct conversion. However, clear() will not clear the underlying string object of stringstream.

3. You can use the s.str(“”) method to set the underlying string object to an “” empty string.

4. You can use s.str() to make stringstream return its underlying string object.