The next challenge is to provide a method that confirms zip codes for the United States. The method needs to accommodate punctuation, a space, or no delimiter at all between the five-digit and four-digit parts of the zip code. It needs to accommodate zip codes that are only five digits long. Suddenly, there's requirements creep: It now needs to validate zip codes for Canada, the United Kingdom, Argentina, Sweden, Japan, and the Netherlands as well.
The first thing I do is search the Web for patterns, starting at http://www.regexlib.com. This returns regular expressions for all of the countries previously mentioned. Next, I take those regular expressions and create entries in the regex.properties file, so I can use the RegexProperties class from Chapter 4.
The point of doing so, of course, is to externalize the expressions themselves and to avoid having to double-delimit special characters. I decide to use intelligent keys for the property keys. That is, I'm anticipating that I'll have access to the country code for each of these regex patterns. Therefore, I can define the property file keys based on that country code. For example, since the country code for Japan is JP, I define the key to the zip code pattern for Japan as zipJP. Listing 5-2 summarizes the entries made to the regex.properties file.
![]() |
#Japanese postal codes zipJP=^\d{3}-\d{4}$ #US postal codes zipUS=^\d{5}\p{Punct}?\s?(?:\d{4})?$ #Dutch postal code zipNL=^[0-9]{4}\s*[a-zA-Z]{2}$ #Argentinean postal code zipAR=^\d{3}-\d{4}$ #Swedish postal code zipSE=^(s-|S-){0,1}[0-9]{3}\s?[0-9]{2}$ #Canadian postal code zipCA=^([A-Z]\d[A-Z]\s\d[A-Z]\d)$ #UK postal code zipUK=^[a-zA-Z]{1,2}[0-9][0-9A-Za-z]{0,1} {0,1}[0-9][A-Za-z]{2}$
![]() |
Finally, I write the code. The algorithm is to look up the appropriate regex for a given country given the appropriate country code, apply the pattern, and return true or false as appropriate. Listing 5-3 shows the code that does this.
![]() |
01 import java.io.*; 02 import java.util.logging.Logger; 03 import java.util.regex.*; 04 /** 05 *Validates zip codes from the given country. 06 *@author M Habibi 07 */ 08 public class MatchZipCodes{ 09 private static Logger log = Logger.getAnonymousLogger(); 10 private static final String ZIP_PATTERN="zip"; 11 private static RegexProperties regexProperties; 12 //load the regex properties file 13 //do this at the class level 14 static 15 { 16 try 17 { 18 regexProperties = new RegexProperties(); 19 regexProperties.load("../regex.properties"); 20 } 21 catch(Exception e) 22 { 23 e.printStackTrace(); 24 } 25 } 26 public static void main(String args[]){ 27 String msg = "usage: java MatchZipCodes countryCode Zip"; 28 if (args != null && args.length == 2) 29 msg = ""+isZipValid(args[0],args[1]); 30 //output either the usage message, or the results 31 //of running the isZipValid method 32 System.out.println(msg); 33 } 34 /** 35 * Confirms that the format for the given zip code is valid. 36 * @param the <code>String</code> countryCode 37 * @param the <code>String</code> zip 38 * @return <code>boolean</code> 39 * 40 * @author M Habibi 41 */ 42 public static boolean isZipValid(String countryCode, String zip) 43 { 44 boolean retval=false; 45 //use the country code to form a unique into the regex 46 //properties file 47 String zipPatternKey = ZIP_PATTERN + countryCode.toUpperCase(); 48 //extract the regex pattern for the given country code 49 String zipPattern = regexProperties.getProperty(zipPatternKey); 50 //if there was some sort of problem, don't bother trying 51 //to execute the regex 52 if (zipPattern != null) 53 retval = zip.trim().matches(zipPattern); 54 else 55 { 56 String msg = "regex for country code "+countryCode; 57 msg+= " not found in property file "; 58 log.warning(msg); 59 } 60 //create log report 61 String msg = "regex="+zipPattern + 62 "\nzip="+zip+"\nCountryCode="+ 63 countryCode+"\nmatch result="+retval; 64 log.finest(msg); 65 return retval; 66 } 67 }
![]() |
Outside of the comments and such, the real work in this method is done in three lines. Line 47 forms the proper key based on the country code:
For example, zipPatternKey equals zipUS for the US country code. Next, line 49 extracts the relevant pattern based on that key:
49 String zipPattern = regexProperties.getProperty(zipPatternKey);
Line 53 actually compares the pattern against the key:
53 retval = zip.trim().matches(zipPattern);
The only regex change I made in this example was to make the actual pattern just a little more memory efficient and a little more lenient, as shown in Table 5-4. Specifically, leniency means that the pattern will accept any punctuation, a space, or no delimiter at all between the first five digits and the last four digits of a U.S. zip code. The pattern will also accept five digits as a sufficient U.S. zip code.
Because the regex patterns are externalized, they can be tweaked later to become more accommodating for the various regions. Better yet, more country codes can be added without requiring code changes: Simply add the appropriate entries to the regex.properties file.
The point here is that even using generic regex patterns found online, I still have a very Java-like flavor to the code. It's modular, adaptable, scalable, and clear.