- 4.1 Introduction
- 4.2 Essentials of Counter-Controlled Iteration
- 4.3 for Iteration Statement
- 4.4 Examples Using the for Statement
- 4.5 Application: Summing Even Integers
- 4.6 Application: Compound-Interest Calculations
- 4.7 do... while Iteration Statement
- 4.8 switch Multiple-Selection Statement
- 4.9 C++17 Selection Statements with Initializers
- 4.10 break and continue Statements
- 4.11 Logical Operators
- 4.12 Confusing the Equality (==) and Assignment (=) Operators
- 4.13 Objects-Natural Case Study: Using the miniz-cpp Library to Write and Read ZIP files
- 4.14 C++20 Text Formatting with Field Widths and Precisions
- 4.15 Wrap-Up
4.13 Objects-Natural Case Study: Using the miniz-cpp Library to Write and Read ZIP files7
Perf Data compression reduces the size of data—typically to save memory, to save secondary storage space or to transmit data over the Internet faster by reducing the number of bytes. Lossless data-compression algorithms compress data in a manner that does not lose information—the data can be uncompressed and restored to its original form. Lossy data-compression algorithms permanently discard information. Such algorithms are often used to compress images, audio and video. For example, when you watch streaming video online, the video is often compressed ahead of time using a lossy algorithm to minimize the total bytes transferred over the Internet. Though some of the video data is discarded, a lossy algorithm compresses the data in a manner such that most people do not notice the removed information as they watch the video. The video quality is still “pretty good.”
ZIP Files
You’ve probably used ZIP files—if not, you almost certainly will. The ZIP file format8 is a lossless compression9 format that has been in use for over 30 years. Lossless compression algorithms use various techniques for compressing data—such as
replacing duplicate patterns, such as text strings in a document or pixels in an image, with references to a single copy, and
replacing a group of image pixels that have the same color with one pixel of that color and a count (known as “run-length encoding”).
ZIP is used to compress files and directories into a single file, known as an archive file. ZIP files are often used to distribute software faster over the Internet. Today’s operating systems typically have built-in support for creating ZIP files and extracting their contents.
Open-Source miniz-cpp Library
Many open-source libraries support programmatic manipulation of ZIP archive files and other popular archive-file formats, such as TAR, RAR and 7-Zip.10 Figure 4.11 continues our Objects-Natural presentation by using objects of the open-source miniz-cpp11, 12 library’s class zip_file to create and read ZIP files. The miniz-cpp library is a “header-only library”—it’s defined in header file zip_file.hpp, which you can simply place in the same folder as this example and include the header in your program (line 5). We provide the library in the examples folder’s libraries/miniz-cpp subfolder. Header files are discussed in depth in Chapter 9.
1 // fig04_11.cpp 2 // Using the miniz-cpp header-only library to write and read a ZIP file. 3 #include <iostream> 4 #include <string> 5 #include "zip_file.hpp" 6 using namespace std; 7
Fig. 4.11 Using the miniz-cpp header-only library to write and read a ZIP file.
Inputting a Line of Text from the User with getline
The getline function call reads all the characters you type until you press Enter:
8 int main() { 9 cout << "Enter a ZIP file name: "; 10 string zipFileName; 11 getline(cin, zipFileName); // inputs a line of text 12
Enter a ZIP file name: c:\users\useraccount\Documents\test.zip
Here we use getline to read from the user the location and name of a file, and store it in the string variable zipFileName. Like class string, getline requires the <string> header and belongs to namespace std.
Creating Sample Content to Write an Individual File in the ZIP File
The following statement creates a lengthy string named content consisting of sentences from this chapter’s introduction:
13 // string literals separated only by whitespace are combined 14 // into a single string by the compiler 15 string content{ 16 "This chapter introduces all but one of the remaining control " 17 "statements--the for, do...while, switch, break and continue " 18 "statements. We explore the essentials of counter-controlled " 19 "iteration. We use compound-interest calculations to begin " 20 "investigating the issues of processing monetary amounts. First, " 21 "we discuss the representational errors associated with " 22 "floating-point types. We use a switch statement to count the " 23 "number of A, B, C, D and F grade equivalents in a set of " 24 "numeric grades. We show C++17's enhancements that allow you to " 25 "initialize one or more variables of the same type in the " 26 "headers of if and switch statements."}; 27
We’ll use the miniz-cpp library to write this string as a text file that will be compressed into a ZIP file. Each string literal in the preceding statement is separated from the next only by whitespace. The C++ compiler automatically assembles such string literals into a single string literal, which we use to initialize the string variable content. The following statement outputs the length of content (632 bytes).
28 cout << "\ncontent.length(): " << content.length(); 29
content.length(): 632
Creating a zip_file Object
The miniz-cpp library’s zip_file class—located in the library’s miniz_cpp namespace—is used to create a ZIP file. The statement
30 miniz_cpp::zip_file output; // create zip_file object 31
creates the zip_file object output, which will perform the ZIP operations to create the archive file.
Creating a File in the zip_file Object and Saving That Object to Disk
Line 33 calls output’s writestr member function, which creates one file ("intro.txt") in the ZIP archive containing the text in content. Line 34 calls output’s save member function to store the output object’s contents in the file specified by zipFileName:
32 // write content into a text file in output 33 output.writestr("intro.txt", content); // create file in ZIP 34 output.save(zipFileName); // save output to zipFileName 35
ZIP Files Appear to Contain Random Symbols
ZIP is a binary format, so if you open the compressed file in a text editor, you’ll see mostly gibberish. Below is what the file looks like in the Windows Notepad text editor:
Reading the Contents of the ZIP File
You can locate the ZIP file on your system and extract (decompress) its contents to confirm that the ZIP file was written correctly. The miniz-cpp library also supports reading and processing a ZIP file’s contents programmatically. The following statement creates a zip_file object named input and initializes it with the name of a ZIP file:
36 miniz_cpp::zip_file input{zipFileName}; // load zipFileName 37
This reads the corresponding ZIP archive’s contents. We can then use the zip_file object’s member functions to interact with the archived files.
Displaying the Name and Contents of the ZIP File
The following statements call input’s get_filename and printdir member functions to display the ZIP’s file name and a directory listing of the ZIP file’s contents, respectively.
38 // display input's file name and directory listing 39 cout << "\n\nZIP file's name: " << input.get_filename() 40 << "\n\nZIP file's directory listing:\n"; 41 input.printdir(); 42
ZIP file's name: c:\users\useraccount\Documents\test.zip ZIP file's directory listing: Length Date Time Name --------- ---------- ----- ---- 632 11/28/2021 16:48 intro.txt --------- ------- 632 1 file
The output shows that the ZIP archive contains the file intro.txt and that the file’s length is 632, which matches that of the string content we wrote to the file earlier.
Getting and Displaying Information About a Specific File in the ZIP Archive
Line 44 declares and initializes the zip_info object info:
43 // display info about the compressed intro.txt file 44 miniz_cpp::zip_info info{input.getinfo("intro.txt")}; 45
Calling input’s getinfo member function returns a zip_info object (from namespace miniz_cpp) for the specified file in the archive. Sometimes objects expose data so that you can access it directly using the object’s name and a dot (.) operator. For example, the object info contains information about the archive’s intro.txt file, including the file’s name (info.filename), its uncompressed size (info.file_size) and its compressed size (info.compress_size):
46 cout << "\nFile name: " << info.filename 47 << "\nOriginal size: " << info.file_size 48 << "\nCompressed size: " << info.compress_size; 49
File name: intro.txt Original size: 632 Compressed size: 360
Note that intro.txt’s compressed size is 360 bytes—43% smaller than the original file. Compression amounts vary considerably, based on the type of content being compressed.
Extracting "intro.txt" and Displaying Its Original Contents
You can extract the original contents of a compressed file from the ZIP archive. Here we use the input object’s read member function, passing the zip_info object (info) as an argument. This returns as a string the contents of the file represented by the object info:
50 // original file contents 51 string extractedContent{input.read(info)}; 52
We output extractedContent to show that it matches the original string content that we “zipped up.” This was indeed a lossless compression:
53 cout << "\n\nOriginal contents of intro.txt:\n" 54 << extractedContent << "\n"; 55 }
Original contents of intro.txt: This chapter introduces all but one of the remaining control statements--the for, do...while, switch, break and continue statements. We explore the essentials of counter-controlled iteration. We use compound-interest calculations to begin investigating the issues of processing monetary amounts. First, we discuss the representational errors associated with floating-point types. We use a switch statement to count the number of A, B, C, D and F grade equivalents in a set of numeric grades. We show C++17's enhancements that allow you to initialize one or more variables of the same type in the headers of if and switch statements.