Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
B.Eckel - Thinking in C++, Vol.2, 2nd edition.pdf
Скачиваний:
50
Добавлен:
08.05.2013
Размер:
2.09 Mб
Скачать

o << nl << "[{[" << name << "]}]" << nl

<<"[([" << nl << value << nl << "])]"

<<nl;

//Delimiters were added to aid parsing of

//the resulting text file.

}

} ///:~

The program is designed to be as generic as possible, but if you want to change something it is most likely the way that the data is stored in a file (for example, you may want to store it in a comma-separated ASCII format so that you can easily read it into a spreadsheet). You can make changes to the storage format by modifying store( ), and to the way the data is displayed by modifying show( ).

main( ) begins using the same three lines you’ll start with for any POST program. The rest of the program is similar to mlm.cpp because it looks at the “test-field” and email-address” (checking it for correctness). The file name combines the user’s email address and the current date and time in hex – notice that sprintf( ) is used because it has a convenient way to convert a value to a hex representation. The entire file and path information is stored in the file, along with all the data from the form, which is tagged as it is stored so that it’s easy to parse (you’ll see a program to parse the files a bit later). All the information is also sent back to the user as a simply-formatted HTML page, along with the reminder, if there is one. If “mail-copy” exists and is not “no,” then the names in the “mail-copy” value are parsed and an email is sent to each one containing the tagged data. Finally, if there is a “confirmation” field, the value selects the type of confirmation (there’s only one type implemented here, but you can easily add others) and the command is built that passes the generated data file to the program (called ProcessApplication.exe). That program will be created in the next section.

Parsing the data files

You now have a lot of data files accumulating on your Web site, as people sign up for whatever you’re offering. Here’s what one of them might look like:

//:! C07:TestData.txt

///{/home/eckel/super-cplusplus-workshop- registration/Bruce@EckelObjects.com35B589A0.txt From[Bruce@EckelObjects.com]

[{[subject-field]}] [([

super-cplusplus-workshop-registration ])]

[{[Date-of-event]}] [([

Appendix B: Programming Guidelines

566

Sept 2-4 ])]

[{[name]}]

[([

Bruce Eckel ])]

[{[street]}]

[([

20 Sunnyside Ave, Suite A129 ])]

[{[city]}]

[([

Mill Valley ])]

[{[state]}]

[([ CA ])]

[{[country]}]

[([ USA ])]

[{[zip]}]

[([

94941

])]

[{[busphone]}]

[([ 415-555-1212 ])]

///:~

This is a brief example, but there are as many fields as you have on your HTML form. Now, if your event is compelling you’ll have a whole lot of these files and what you’d like to do is automatically extract the information from them and put that data in any format you’d like.

For example, the ProcessApplication.exe program mentioned above will use the data in an email confirmation message. You’ll also probably want to put the data in a form that can be

Appendix B: Programming Guidelines

567

easily brought into a spreadsheet. So it makes sense to start by creating a general-purpose tool that will automatically parse any file that is created by ExtractInfo.cpp:

//: C10:FormData.h #include <string> #include <iostream> #include <fstream> #include <vector> using namespace std;

class DataPair : public pair<string, string> { public:

DataPair() {}

DataPair(istream& in) { get(in); } DataPair& get(istream& in); operator bool() {

return first.length() != 0;

}

};

class FormData : public vector<DataPair> { public:

string filePath, email;

// Parse the data from a file: FormData(char* fileName);

void dump(ostream& os = cout);

string operator[](const string& key); }; ///:~

The DataPair class looks a bit like the CGIpair class, but it’s simpler. When you create a DataPair, the constructor calls get( ) to extract the next pair from the input stream. The operator bool indicates an empty DataPair, which usually signals the end of an input stream.

FormData contains the path where the original file was placed (this path information is stored within the file), the email address of the user, and a vector<DataPair> to hold the information. The operator[ ] allows you to perform a map-like lookup, just as in CGImap.

Here are the definitions:

//: C10:FormData.cpp {O} #include "FormData.h" #include "../require.h"

DataPair& DataPair::get(istream& in) { first.erase(); second.erase(); string ln;

Appendix B: Programming Guidelines

568

getline(in,ln);

while(ln.find("[{[") == string::npos) if(!getline(in, ln)) return *this; // End

first = ln.substr(3, ln.find("]}]") - 3); getline(in, ln); // Throw away [([ while(getline(in, ln))

if(ln.find("])]") == string::npos) second += ln + string(" ");

else

return *this;

}

FormData::FormData(char* fileName) { ifstream in(fileName);

assure(in, fileName); require(getline(in, filePath) != 0);

//Should be start of first line: require(filePath.find("///{") == 0); filePath = filePath.substr(strlen("///{")); require(getline(in, email) != 0);

//Should be start of 2nd line: require(email.find("From[") == 0); int begin = strlen("From[");

int end = email.find("]"); int length = end - begin;

email = email.substr(begin, length);

//Get the rest of the data:

DataPair dp(in); while(dp) {

push_back(dp); dp.get(in);

}

}

string FormData::operator[](const string& key) { iterator i = begin();

while(i != end()) { if((*i).first == key)

return (*i).second; i++;

}

return string(); // Empty string == not found

}

Appendix B: Programming Guidelines

569

void FormData::dump(ostream& os) {

os << "filePath = " << filePath << endl; os << "email = " << email << endl; for(iterator i = begin(); i != end(); i++)

os << (*i).first << " = " << (*i).second << endl;

} ///:~

The DataPair::get( ) function assumes you are using the same DataPair over and over (which is the case, in FormData::FormData( )) so it first calls erase( ) for its first and second strings. Then it begins parsing the lines for the key (which is on a single line and is denoted by the “[{[” and “]}]”) and the value (which may be on multiple lines and is denoted by a begin-marker of “[([” and an end-marker of “])]”) which it places in the first and second members, respectively.

The FormData constructor is given a file name to open and read. The FormData object always expects there to be a file path and an email address, so it reads those itself before getting the rest of the data as DataPairs.

With these tools in hand, extracting the data becomes quite easy:

//: C10:FormDump.cpp //{L} FormData #include "FormData.h"

#include "../require.h"

int main(int argc, char* argv[]) { requireArgs(argc, 1);

FormData fd(argv[1]); fd.dump();

} ///:~

The only reason that ProcessApplication.cpp is busier is that it is building the email reply. Other than that, it just relies on FormData:

//: C10:ProcessApplication.cpp //{L} FormData

#include "FormData.h" #include "../require.h" using namespace std;

const string from("Bruce@EckelObjects.com"); const string replyto("Bruce@EckelObjects.com"); const string basepath("/home/eckel");

Appendix B: Programming Guidelines

570

int main(int argc, char* argv[]) { requireArgs(argc, 1);

FormData fd(argv[1]); char tfname[L_tmpnam];

tmpnam(tfname); // Create a temporary file name string tempfile(basepath + tfname + fd.email); ofstream reply(tempfile.c_str());

assure(reply, tempfile.c_str());

reply << "This message is to verify that you " "have been added to the list for the "

<< fd["subject-field"] << ". Your signup " "form included the following data; please " "ensure it is correct. You will receive " "further updates via email. Thanks for your " "interest in the class!" << endl;

FormData::iterator i;

for(i = fd.begin(); i != fd.end(); i++) reply << (*i).first << " = "

<< (*i).second << endl; reply.close();

// "fastmail" only available on Linux/Unix: string command("fastmail -F " + from +

" -r " + replyto + " -s \"" + fd["subject-field"] + "\" " + tempfile + " " + fd.email);

system(command.c_str()); // Wait to finish remove(tempfile.c_str()); // Erase the file

} ///:~

This program first creates a temporary file to build the email message in. Although it uses the Standard C library function tmpnam( ) to create a temporary file name, this program takes the paranoid step of assuming that, since there can be many instances of this program running at once, it’s possible that a temporary name in one instance of the program could collide with the temporary name in another instance. So to be extra careful, the email address is appended onto the end of the temporary file name.

The message is built, the DataPairs are added to the end of the message, and once again the Linux/Unix fastmail command is built to send the information. An interesting note: if, in Linux/Unix, you add an ampersand (&) to the end of the command before giving it to system( ), then this command will be spawned as a background process and system( ) will immediately return (the same effect can be achieved in Win32 with start). Here, no ampersand is used, so system( ) does not return until the command is finished – which is a good thing, since the next operation is to delete the temporary file which is used in the command.

Appendix B: Programming Guidelines

571

The final operation in this project is to extract the data into an easily-usable form. A spreadsheet is a useful way to handle this kind of information, so this program will put the data into a form that’s easily readable by a spreadsheet program:

//: C10:DataToSpreadsheet.cpp //{L} FormData

#include "FormData.h" #include "../require.h" #include <string>

using namespace std;

string delimiter("\t");

int main(int argc, char* argv[]) { for(int i = 1; i < argc; i++) {

FormData fd(argv[i]);

cout << fd.email << delimiter; FormData::iterator i;

for(i = fd.begin(); i != fd.end(); i++) if((*i).first != "workshop-suggestions")

cout << (*i).second << delimiter; cout << endl;

}

} ///:~

Common data interchange formats use various delimiters to separate fields of information. Here, a tab is used but you can easily change it to something else. Also note that I have checked for the “workshop-suggestions” field and specifically excluded that, because it tends to be too long for the information I want in a spreadsheet. You can make another version of this program that only extracts the “workshop-suggestions” field.

This program assumes that all the file names are expanded on the command line. Using it under Linux/Unix is easy since file-name global expansion (“globbing”) is handled for you. So you say:

DataToSpreadsheet *.txt >> spread.out

In Win32 (at a DOS prompt) it’s a bit more involved, since you must do the “globbing” yourself:

For %f in (*.txt) do DataToSpreadsheet %f >> spread.out

This technique is generally useful for writing Win32/DOS command lines.

Appendix B: Programming Guidelines

572

Соседние файлы в предмете Численные методы
  • #
    08.05.20133.99 Mб22A.Menezes, P.van Oorschot,S.Vanstone - HANDBOOK OF APPLIED CRYPTOGRAPHY.djvu
  • #
  • #
    08.05.20135.91 Mб24B.Eckel - Thinking in Java, 3rd edition (beta).pdf
  • #
  • #
    08.05.20136.09 Mб17D.MacKay - Information Theory, Inference, and Learning Algorithms.djvu
  • #
    08.05.20133.85 Mб15DIGITAL Visual Fortran ver.5.0 - Programmers Guide to Fortran.djvu
  • #
    08.05.20131.84 Mб12E.A.Lee, P.Varaiya - Structure and Interpretation of Signals and Systems.djvu