Verifying and viewing the data
To check the validity of the data stored in binary format, you can read it into memory with the read( )member function for input streams, and compare it to the text file created earlier by Datagen.cpp. The following example just writes the formatted results to cout, but you can redirect this to a file and then use a file comparison utility to verify that it is identical to the original .
//: C04:Datascan.cpp
// Test data generator
//{L} DataLogger
#include
#include
#include "DataLogger.h"
#include "../require.h"
using namespace std;
int main() {
ifstream bindata("data.bin", ios::binary);
assure(bindata, "data.bin");
DataPoint d;
while (bindata.read(reinterpret_cast(&d),
sizeof d))
cout << d << endl;
} ///:~
The software industry is now a healthy, worldwide economic market, and applications that can run in various languages and cultures are in demand. As early as the late 1980s, the C Standards Committee added support for non-U.S. formatting conventions with their locale mechanism. A locale is a set of preferences for displaying certain entities such as dates and monetary quantities. In the 1990s, the C Standards Committee approved an addendum to Standard C that specified functions to handle wide characters (denoted by the type wchar_t), which allow support for character sets other than ASCII and its commonly used Western European extensions. Although the size of a wide character is not specified, some platforms implement them as 32-bit quantities, so they can hold the encodings specified by the Unicode Consortium, as well as mappings to multi-byte characters sets defined by Asian standards bodies. C++ has integrated support for both wide characters and locales into the iostreams library .
A wide stream is a simply a stream class that handles wide characters. All the examples so far (except for the last traits example in Chapter 3) have used narrow streams, meaning streams that hold instances of char. Since stream operations are essentially the same no matter the underlying character type, they are encapsulated generically as templates. As we mentioned earlier, all input streams, for example, are connected somehow to the basic_istreamclass template, which is defined as follows:
template >
class basic_istream {…};
In fact, all input stream types are specializations of this template, according to the following type definitions:
typedef basic_istream istream;
typedef basic_istream wistream;
typedef basic_ifstream ifstream;
typedef basic_ifstream wifstream;
typedef basic_istringstream istringstream;
typedef basic_istringstream wistringstream;
All other stream types are defined in similar fashion.
In a "perfect" world, this is all you’d have to do to have streams of different character types. In reality, things aren’t that simple. The reason is that the character-processing functions provided for charand wchar_tdon’t have the same names. To compare two narrow strings, for example, you use the strcmp( )function. For wide characters, that function is named wcscmp( ). (Remember these originated in C, which does not have function overloading, hence unique names are a must.) For this reason, a generic stream can’t just call strcmp( )in response to a comparison operator. There needs to be a way for the correct low-level functions to be called automatically .
The principle that guides the solution is well known. You simply "factor out" the differences into a new abstraction. The operations you can perform on characters have been abstracted into the char_traitstemplate, which has predefined specializations for char and wchar_t, as we discussed at the end of the previous chapter. To compare two strings, then, basic_stringjust calls traits::compare( )(remember that traitsis the second template parameter), which in turn calls either strcmp( )or wcscmp( ), depending on which specialization is being used (transparent to basic_string, of course) .
You only need to be concerned about char_traitsif you access the low-level character processing functions; most of the time you don’t care. Consider, however, making your inserters and extractors more robust by defining them as templates, just in case someone wants to use them on a wide stream.
To illustrate, recall again the Dateclass inserter from the beginning of this chapter. We originally declared it as:
ostream& operator<<(ostream&, const Date&);
This accommodates only narrow streams. To make it generic, we simply make it a template based on basic_ostream:
template
std::basic_ostream&
operator<<(std::basic_ostream& os,
const Date& d) {
charT fillc = os.fill(os.widen('0'));
charT dash = os.widen('-');
os << setw(2) << d.month << dash
<< setw(2) << d.day << dash
<< setw(4) << d.year;
os.fill(fillc);
return os;
}
Notice that we also have to replace charwith the template parameter charTin the declaration of fillc, since it could be either charor wchar_t, depending on the template instantiation being used .
Since you don’t know when you’re writing the template which type of stream you have, you need a way to automatically convert character literals to the correct size for the stream. This is the job of the widen( )member function. The expression widen('-'), for example, converts its argument to L’-’(the literal syntax equivalent to the conversion wchar_t(‘-’)) if the stream is a wide stream and leaves it alone otherwise. There is also a narrow( )function that converts to a charif needed .
We can use widen( )to write a generic version of the nlmanipulator we presented earlier in the chapter.
template
basic_ostream&
nl(basic_ostream& os) {
return os << charT(os.widen('\n'));
}
Perhaps the most notable difference in typical numeric computer output from country to country is the punctuator used to separate the integer and fractional parts of a real number. In the United States, a period denotes a decimal point, but in much of the world, a comma is expected instead. It would be quite inconvenient to do all your own formatting for locale-dependent displays. Once again, creating an abstraction that handles these differences solves the problem.
That abstraction is the locale . All streams have an associated locale object that they use for guidance on how to display certain quantities for different cultural environments. A locale manages the categories of culture-dependent display rules, which are defined as follows:
Category |
Effect |
collate |
allows comparing strings according to different, supported collating sequences |
ctype |
abstracts the character classification and conversion facilities found in |
monetary |
supports different displays of monetary quantities |
numeric |
supports different display formats of real numbers, including radix (decimal point) and grouping (thousands) separators |
time |
supports various international formats for display of date and time |
messages |
scaffolding to implement context-dependent message catalogs (such as for error messages in different languages) |
The following program illustrates basic locale behavior:
Читать дальше