O''''Reilly Network For Information About''''s Book part 59 potx

5 230 0
O''''Reilly Network For Information About''''s Book part 59 potx

Đang tải... (xem toàn văn)

Thông tin tài liệu

Usage To begin using Boost.Regex, you need to include the header "boost/regex.hpp". Regex is one of the two libraries (the other one is Boost.Signals) covered in this book that need to be separately compiled. You'll be glad to know that after you've built Boostthis is a one-liner from the command promptlinking is automatic (for Windows-based compilers anyway), so you're relieved from the tedium of figuring out which lib file to use. The first thing you need to do is to declare a variable of type basic_regex. This is one of the core classes in the library, and it's the one that stores the regular expression. Creating one is simple; just pass a string to the constructor containing the regular expression you want to use. boost::regex reg("(A.*)"); This regular expression contains three interesting features of regular expressions. The first is the enclosing of a subexpression within parenthesesthis makes it possible to refer to that subexpression later on in the same regular expression or to extract the text that matches it. We'll talk about this in detail later on, so don't worry if you don't yet see how that's useful. The second feature is the wildcard character, the dot. The wildcard has a very special meaning in regular expressions; it matches any character. Finally, the expression uses a repeat, *, called the Kleene star, which means that the preceding expression may match zero or more times. This regular expression is ready to be used in one of the algorithms, like so: bool b=boost::regex_match( "This expression could match from A and beyond.", reg); As you can see, you pass the regular expression and the string to be parsed to the algorithm regex_match. The result of calling the function is true if there is an exact match for the regular expression; otherwise, it is false. In this case, the result is false, because regex_match only returns true when all of the input data is successfully matched by the regular expression. Do you see why that's not the case for this code? Look again at the regular expression. The first character is a capital A, so that's obviously the first character that could ever match the expression. So, a part of the input"A and beyond."does match the expression, but it does not exhaust the input. Let's try another input string. bool b=boost::regex_match( "As this string starts with A, does it match? ", reg); This time, regex_match returns true. When the regular expression engine matches the A, it then goes on to see what should follow. In our regex, A is followed by the wildcard, to which we have applied the Kleene star, meaning that any character is matching any number of times. Thus, the parsing starts to consume the rest of the input string, and matches all the rest of the input. Next, let's see how we can put regexes and regex_match to work with data validation. Validating Input A common scenario where regular expressions are used is in validating the format of input data. Applications often require that input adhere to a certain structure. Consider an application that accepts input that must come in the form "3 digits, a word, any character, 2 digits or the string "N/A," a space, then the first word again." Coding such validations manually is both tedious and error prone, and furthermore, these formats are typically exposed to changing requirements; before you know it, some variation of the format needs to be supported, and your carefully crafted parser suddenly needs to be changed and debugged. Let's assemble a regular expression that can validate such input correctly. First, we need an expression that matches exactly 3 digits. There's a special shortcut for digits, \d, that we'll use. To have it repeated 3 times, there's a special kind of repeat called the bounds operator, which encloses the bounds in curly braces. Putting these two together, here's the first part of our regular expression. boost::regex reg("\\d{3}"); Note that we need to escape the escape character, so the shortcut \d becomes \\d in our string. This is because the compiler consumes the first backslash as an escape character; we need to escape the backslash so a backslash actually appears in the regular expression string. Next, we need a way to define a wordthat is, a sequence of characters, ended by any character that is not a letter. There is more than one way of accomplishing this, but we will do it using the regular expression features character classes (also called character sets) and ranges. A character class is an expression enclosed in square brackets. For example, a character class that matches any one of the characters a, b, and c, looks like this: [abc]. Using a range to accomplish the same thing, we write it like so: [a-c]. For a character class that encompasses all characters, we could go slightly crazy and write it like [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ], but we won't; we'll use ranges instead: [a-zA-Z]. It should be noted that using ranges like this can make one dependent on the locale that is currently in use, if the basic_regex::collate flag is turned on for the regular expression. Using these tools and the repeat +, which means that the preceding expression can be repeated, but must exist at least once, we're now ready to describe a word. boost::regex reg("[a-zA-Z]+"); That regular expression works, but because it is so common, there is an even simpler way to represent a word: \w. That operator matches all word characters, not just the ASCII ones, so not only is it shorter, it is better for internationalization purposes. The next character should be exactly one of any character, which we know is the purpose of the dot. boost::regex reg("."); The next part of the input is 2 digits or the string "N/A." To match that, we need to use a feature called alternatives. Alternatives match one of two or more subexpressions, with each alternative separated from the others by |. Here's how it looks: boost::regex reg("(\\d{2}|N/A)"); Note that the expression is enclosed in parentheses, to make sure that the full expressions are considered as the two alternatives. Adding a space to the regular expression is simple; there's a shortcut for it: \s. Putting together everything we have so far gives us the following expression: boost::regex reg("\\d{3}[a-zA-Z]+.(\\d{2}|N/A)\\s"); Now things get a little trickier. We need a way to validate that the next word in the input data exactly matches the first word (the one we capture using the expression [a-zA-Z]+). The key to accomplish this is to use a back reference, which is a reference to a previous subexpression. For us to be able to refer to the expression [a-zA-Z]+, we must first enclose it in parentheses. That makes the expression ([a- zA-Z]+) the first subexpression in our regular expression, and we can therefore create a back reference to it using the index 1. That gives us the full regular expression for "3 digits, a word, any character, 2 digits or the string "N/A," a space, then the first word again": boost::regex reg("\\d{3}([a-zA-Z]+).(\\d{2}|N/A)\\s\\1"); Good work! Here's a simple program that makes use of the expression with the algorithm regex_match, validating two sample input strings. #include <iostream> #include <cassert> #include <string> #include "boost/regex.hpp" int main() { // 3 digits, a word, any character, 2 digits or "N/A", // a space, then the first word again boost::regex reg("\\d{3}([a-zA-Z]+).(\\d{2}|N/A)\\s\\1"); std::string correct="123Hello N/A Hello"; std::string incorrect="123Hello 12 hello"; assert(boost::regex_match(correct,reg)==true); assert(boost::regex_match(incorrect,reg)==false); } The first string, 123Hello N/A Hello, is correct; 123 is 3 digits, followed by any character (a space), Hello is a word, then another space, and finally the word Hello is repeated. The second string is incorrect, because the word Hello is not repeated exactly. By default, regular expressions are case-sensitive, and the back reference therefore does not match. One of the keys in crafting regular expressions is successfully decomposing the problem. When looking at the final expression that you just created, it can seem quite intimidating to the untrained eye. However, when decomposing the expression into smaller components, it's not very complicated at all. Searching We shall now take a look at another of Boost.Regex's algorithms, regex_search. The difference from regex_match is that regex_search does not require that all of the input data matches, but only that part of it does. For this exposition, consider the problem of a programmer who expects to have forgotten one or two calls to delete in his program. Although he realizes that it's by no means a foolproof test, he decides to count the number of occurrences of new and delete and see if the numbers add up. The regular expression is very simple; we have two alternatives, new and delete. boost::regex reg("(new)|(delete)"); There are two reasons for us to enclose the subexpressions in parentheses: one is that we must do so in order to form the two groups for our alternatives. The other reason is that we will want to refer to these subexpressions when calling regex_search, to enable us to determine which of the alternatives was actually matched. We will use an overload of regex_search that also accepts an argument of type match_results. When regex_search performs its matching, it reports subexpression matches through an object of type match_results. The class template match_results is parameterized on the type of iterator that applies to the input sequence. template <class Iterator, class Allocator=std::allocator<sub_match<Iterator> > class match_results; typedef match_results<const char*> cmatch; typedef match_results<const wchar_t> wcmatch; typedef match_results<std::string::const_iterator> smatch; typedef match_results<std::wstring::const_iterator> wsmatch; We will use std::string, and are therefore interested in the typedef smatch, which is short for match_results<std::string::const_iterator>. When regex_search returns true, the reference to match_results that is passed to the function contains the results of the subexpression matches. Within match_results, there are indexed sub_matches for each of the subexpressions in the regular expression. Let's see what we have so far that can help our confused programmer assess the calls to new and delete. boost::regex reg("(new)|(delete)"); boost::smatch m; std::string s= "Calls to new must be followed by delete. \ . tedious and error prone, and furthermore, these formats are typically exposed to changing requirements; before you know it, some variation of the format needs to be supported, and your carefully. that all of the input data matches, but only that part of it does. For this exposition, consider the problem of a programmer who expects to have forgotten one or two calls to delete in his program reg("(new)|(delete)"); There are two reasons for us to enclose the subexpressions in parentheses: one is that we must do so in order to form the two groups for our alternatives. The other reason

Ngày đăng: 07/07/2014, 08:20

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan