UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

150 lines (145 loc) 27.4 kB
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"><head><title>Parsing and Formatting Text</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Parsing and Formatting Text"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-10-SECT-4"/>Parsing and Formatting Text</h1></div></div></div><p><a id="idx10522" class="indexterm"/> <a id="idx10535" class="indexterm"/> <a id="idx10581" class="indexterm"/>Parsing and formatting text is a large, open-ended topic. So far in this chapter, we’ve looked at only primitive operations on strings—creation, basic editing, searching, and turning simple values into strings. Now we’d like to move on to more structured forms of text. Java has a rich set of APIs for parsing and printing formatted strings, including numbers, dates, times, and currency values. We’ll cover most of these topics in this chapter, but we’ll wait to discuss date and time formatting until <a class="xref" href="ch11.html" title="Chapter 11. Core Utilities">Chapter 11</a>.</p><p>We’ll start with parsing—reading primitive numbers and values as strings and chopping long strings into tokens. Then we’ll go the other way and look at formatting strings and the <code class="literal">java.text</code> package. We’ll revisit the topic of internationalization to see how Java can localize parsing and formatting of text, numbers, and dates for particular locales. Finally, we’ll take a detailed look at regular expressions, the most powerful text-parsing tool Java offers. Regular expressions let you define your own patterns of arbitrary complexity, search for them, and parse them from text.</p><p>We should mention that you’re going to see a great deal of overlap between the new formatting and parsing APIs (<code class="literal">printf</code> and <code class="literal">Scanner</code>) introduced in Java 5.0 and the older APIs of the <code class="literal">java.text</code> package. The new APIs effectively replace much of the old ones and in some ways are easier to use. Nonetheless, it’s good to know about both because so much existing code uses the older APIs.</p><div class="sect2" title="Parsing Primitive Numbers"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-10-SECT-4.1"/>Parsing Primitive Numbers</h2></div></div></div><p><a id="idx10539" class="indexterm"/>In Java, numbers and Booleans are primitive types—not objects. But for each primitive type, Java also defines a <span class="emphasis"><em>primitive wrapper</em></span> class. Specifically, the <a id="I_indexterm10_id727778" class="indexterm"/><code class="literal">java.lang</code> package includes the following classes: <a id="I_indexterm10_id727789" class="indexterm"/><code class="literal">Byte</code>, <a id="I_indexterm10_id727800" class="indexterm"/><code class="literal">Short</code>, <a id="I_indexterm10_id727810" class="indexterm"/><code class="literal">Integer</code>, <a id="I_indexterm10_id727820" class="indexterm"/><code class="literal">Long</code>, <a id="I_indexterm10_id727831" class="indexterm"/><code class="literal">Float</code>, <a id="I_indexterm10_id727841" class="indexterm"/><code class="literal">Double</code>, and <a id="I_indexterm10_id727852" class="indexterm"/><code class="literal">Boolean</code>. We talked about these in <a class="xref" href="ch01.html" title="Chapter 1. A Modern Language">Chapter 1</a>, but we bring them up now because these classes hold static utility methods that know how to parse their respective types from strings. Each of these primitive wrapper classes has a static “parse” method that reads a <code class="literal">String</code> and returns the corresponding primitive type. For example:</p><a id="I_10_tt586"/><pre class="programlisting"> <code class="kt">byte</code> <code class="n">b</code> <code class="o">=</code> <code class="n">Byte</code><code class="o">.</code><code class="na">parseByte</code><code class="o">(</code><code class="s">"16"</code><code class="o">);</code> <code class="kt">int</code> <code class="n">n</code> <code class="o">=</code> <code class="n">Integer</code><code class="o">.</code><code class="na">parseInt</code><code class="o">(</code> <code class="s">"42"</code> <code class="o">);</code> <code class="kt">long</code> <code class="n">l</code> <code class="o">=</code> <code class="n">Long</code><code class="o">.</code><code class="na">parseLong</code><code class="o">(</code> <code class="s">"99999999999"</code> <code class="o">);</code> <code class="kt">float</code> <code class="n">f</code> <code class="o">=</code> <code class="n">Float</code><code class="o">.</code><code class="na">parseFloat</code><code class="o">(</code> <code class="s">"4.2"</code> <code class="o">);</code> <code class="kt">double</code> <code class="n">d</code> <code class="o">=</code> <code class="n">Double</code><code class="o">.</code><code class="na">parseDouble</code><code class="o">(</code> <code class="s">"99.99999999"</code> <code class="o">);</code> <code class="kt">boolean</code> <code class="n">b</code> <code class="o">=</code> <code class="n">Boolean</code><code class="o">.</code><code class="na">parseBoolean</code><code class="o">(</code><code class="s">"true"</code><code class="o">);</code> <code class="c1">// Prior to Java 5.0 use:</code> <code class="kt">boolean</code> <code class="n">b</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Boolean</code><code class="o">(</code><code class="s">"true"</code><code class="o">).</code><code class="na">booleanValue</code><code class="o">();</code></pre><p>Alternately, the <a id="idx10564" class="indexterm"/><code class="literal">java.util.Scanner</code> provides a single API for not only parsing individual primitive types from strings, but reading them from a stream of tokens. This example shows how to use it in place of the preceding wrapper classes:</p><a id="I_10_tt587"/><pre class="programlisting"> <code class="kt">byte</code> <code class="n">b</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"16"</code><code class="o">).</code><code class="na">nextByte</code><code class="o">();</code> <code class="kt">int</code> <code class="n">n</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"42"</code><code class="o">).</code><code class="na">nextInt</code><code class="o">();</code> <code class="kt">long</code> <code class="n">l</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"99999999999"</code><code class="o">).</code><code class="na">nextLong</code><code class="o">();</code> <code class="kt">float</code> <code class="n">f</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"4.2"</code><code class="o">).</code><code class="na">nextFloat</code><code class="o">();</code> <code class="kt">double</code> <code class="n">d</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"99.99999999"</code><code class="o">).</code><code class="na">nextDouble</code><code class="o">();</code> <code class="kt">boolean</code> <code class="n">b</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"true"</code><code class="o">).</code><code class="na">nextBoolean</code><code class="o">();</code></pre><p>We’ll see <code class="literal">Scanner</code> used to parse multiple values from a <code class="literal">String</code> or stream when we discuss tokenizing text later in this chapter.</p><div class="sect3" title="Working with alternate bases"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-10-SECT-4.1.1"/>Working with alternate bases</h3></div></div></div><p>It’s easy to parse integer type numbers (<a id="I_indexterm10_id727941" class="indexterm"/><code class="literal">byte</code>, <a id="I_indexterm10_id727953" class="indexterm"/><code class="literal">short</code>, <a id="I_indexterm10_id727966" class="indexterm"/><code class="literal">int</code>, <code class="literal">long</code>) in alternate numeric bases. You can use the parse methods of the primitive wrapper classes by simply specifying the base as a second parameter:</p><a id="I_10_tt588"/><pre class="programlisting"> <code class="kt">long</code> <code class="n">l</code> <code class="o">=</code> <code class="n">Long</code><code class="o">.</code><code class="na">parseLong</code><code class="o">(</code> <code class="s">"CAFEBABE"</code><code class="o">,</code> <code class="mi">16</code> <code class="o">);</code> <code class="c1">// l = 3405691582</code> <code class="kt">byte</code> <code class="n">b</code> <code class="o">=</code> <code class="n">Byte</code><code class="o">.</code><code class="na">parseByte</code> <code class="o">(</code> <code class="s">"12"</code><code class="o">,</code> <code class="mi">8</code> <code class="o">);</code> <code class="c1">// b = 10</code></pre><p>All methods of the Java 5.0 <code class="literal">Scanner</code> class described earlier also accept a base as an optional argument:</p><a id="I_10_tt589"/><pre class="programlisting"> <code class="kt">long</code> <code class="n">l</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code> <code class="s">"CAFEBABE"</code> <code class="o">).</code><code class="na">nextLong</code><code class="o">(</code> <code class="mi">16</code> <code class="o">);</code> <code class="c1">// l = 3405691582</code> <code class="kt">byte</code> <code class="n">b</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code> <code class="s">"12"</code> <code class="o">).</code><code class="na">nextByte</code><code class="o">(</code> <code class="mi">8</code> <code class="o">);</code> <code class="c1">// b = 10</code></pre><p>You can go the other way and convert a <a id="I_indexterm10_id728014" class="indexterm"/><code class="literal">long</code> or integer value to a string value in a specified base using special static <a id="I_indexterm10_id728028" class="indexterm"/><code class="literal">toString()</code> methods of the <code class="literal">Integer</code> and <code class="literal">Long</code> classes:</p><a id="I_10_tt590"/><pre class="programlisting"> <code class="n">String</code> <code class="n">s</code> <code class="o">=</code> <code class="n">Long</code><code class="o">.</code><code class="na">toString</code><code class="o">(</code> <code class="mi">3405691582L</code><code class="o">,</code> <code class="mi">16</code> <code class="o">);</code> <code class="c1">// s = "cafebabe"</code></pre><p>For convenience, each class also has a static <a id="I_indexterm10_id728063" class="indexterm"/><code class="literal">toHexString()</code> method for working with base 16:</p><a id="I_10_tt591"/><pre class="programlisting"> <code class="n">String</code> <code class="n">s</code> <code class="o">=</code> <code class="n">Integer</code><code class="o">.</code><code class="na">toHexString</code><code class="o">(</code> <code class="mi">255</code> <code class="o">).</code><code class="na">toUpperCase</code><code class="o">();</code> <code class="c1">// s = "FF";</code></pre></div><div class="sect3" title="Number formats"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-10-SECT-4.1.2"/>Number formats</h3></div></div></div><p><a id="I_indexterm10_id728090" class="indexterm"/>The preceding wrapper class parser methods handle the case of numbers formatted using only the simplest English conventions with no frills. If these parse methods do not understand the string, either because it’s simply not a valid number or because the number is formatted in the convention of another language, they throw a <code class="literal">NumberFormatException</code>:</p><a id="I_10_tt592"/><pre class="programlisting"> <code class="c1">// Italian formatting</code> <code class="kt">double</code> <code class="n">d</code> <code class="o">=</code> <code class="n">Double</code><code class="o">.</code><code class="na">parseDouble</code><code class="o">(</code><code class="s">"1.234,56"</code><code class="o">);</code> <code class="c1">// NumberFormatException</code></pre><p>The <code class="literal">Scanner</code> API is smarter and can use <code class="literal">Locale</code>s to parse numbers in specific languages with more elaborate conventions. For example, the <code class="literal">Scanner</code> can handle comma-formatted numbers:</p><a id="I_10_tt593"/><pre class="programlisting"> <code class="kt">int</code> <code class="n">n</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"99,999,999"</code><code class="o">).</code><code class="na">nextInt</code><code class="o">();</code></pre><p>You can specify a <code class="literal">Locale</code> other than the default with the <a id="I_indexterm10_id728154" class="indexterm"/><code class="literal">useLocale()</code> method. Let’s parse that value in Italian now:</p><a id="I_10_tt594"/><pre class="programlisting"> <code class="kt">double</code> <code class="n">d</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"1.234,56"</code><code class="o">).</code><code class="na">useLocale</code><code class="o">(</code> <code class="n">Locale</code><code class="o">.</code><code class="na">ITALIAN</code> <code class="o">).</code><code class="na">nextDouble</code><code class="o">();</code></pre><p>If the <code class="literal">Scanner</code> cannot parse a string, it throws a runtime <code class="literal">InputMismatchException</code>:</p><a id="I_10_tt595"/><pre class="programlisting"> <code class="kt">double</code> <code class="n">d</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code><code class="s">"garbage"</code><code class="o">).</code><code class="na">nextDouble</code><code class="o">();</code> <code class="c1">// InputMismatchException</code></pre><p>Prior to Java 5.0, this kind of parsing was accomplished using the <code class="literal">java.text</code> package with the <a id="I_indexterm10_id728205" class="indexterm"/><code class="literal">NumberFormat</code> class. The classes of the <code class="literal">java.text</code> package also allow you to parse additional types, such as dates, times, and localized currency values, that aren’t handled by the <code class="literal">Scanner</code>. We’ll look at these later in this chapter.<a id="I_indexterm10_id728230" class="indexterm"/></p></div></div><div class="sect2" title="Tokenizing Text"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-10-SECT-4.2"/>Tokenizing Text</h2></div></div></div><p><a id="idx10593" class="indexterm"/> <a id="idx10594" class="indexterm"/>A common programming task involves parsing a string of text into words or “tokens” that are separated by some set of delimiter characters, such as spaces or commas. The first example contains words separated by single spaces. The second, more realistic problem involves comma-delimited fields.</p><a id="I_10_tt596"/><pre class="programlisting"> <code class="n">Now</code> <code class="n">is</code> <code class="n">the</code> <code class="n">time</code> <code class="k">for</code> <code class="n">all</code> <code class="n">good</code> <code class="nf">men</code> <code class="o">(</code><code class="n">and</code> <code class="n">women</code><code class="o">)...</code> <code class="n">Check</code> <code class="n">Number</code><code class="o">,</code> <code class="n">Description</code><code class="o">,</code> <code class="n">Amount</code> <code class="mi">4231</code><code class="o">,</code> <code class="n">Java</code> <code class="n">Programming</code><code class="o">,</code> <code class="mf">1000.00</code></pre><p>Java has several (unfortunately overlapping) APIs for handling situations like this. The most powerful and useful are the <code class="literal">String split()</code> and <code class="literal">Scanner</code> APIs. Both utilize regular expressions to allow you to break the string on arbitrary patterns. We haven’t talked about regular expressions yet, but in order to show you how this works we’ll just give you the necessary magic and explain in detail later in this chapter. We’ll also mention a legacy utility, <code class="literal">java.util.StringTokenizer</code>, which uses simple character sets to split a string. <code class="literal">StringTokenizer</code> is not as powerful, but doesn’t require an understanding of regular expressions.</p><p>The <code class="literal">String split()</code> method accepts a regular expression that describes a delimiter and uses it to chop the string into an array of <code class="literal">String</code>s:</p><a id="I_10_tt597"/><pre class="programlisting"> <code class="n">String</code> <code class="n">text</code> <code class="o">=</code> <code class="s">"Now is the time for all good men"</code><code class="o">;</code> <code class="n">String</code> <code class="o">[]</code> <code class="n">words</code> <code class="o">=</code> <code class="n">text</code><code class="o">.</code><code class="na">split</code><code class="o">(</code><code class="s">"\\s"</code><code class="o">);</code> <code class="c1">// words = "Now", "is", "the", "time", ...</code> <code class="n">String</code> <code class="n">text</code> <code class="o">=</code> <code class="s">"4231, Java Programming, 1000.00"</code><code class="o">;</code> <code class="n">String</code> <code class="o">[]</code> <code class="n">fields</code> <code class="o">=</code> <code class="n">text</code><code class="o">.</code><code class="na">split</code><code class="o">(</code><code class="s">"\\s*,\\s*"</code><code class="o">);</code> <code class="c1">// fields = "4231", "Java Programming", "1000.00"</code></pre><p>In the first example, we used the regular expression <code class="literal">\\s</code>, which matches a single whitespace character (space, tab, or carriage return). The <a id="I_indexterm10_id728348" class="indexterm"/><code class="literal">split()</code> method returned an array of eight strings. In the second example, we used a more complicated regular expression, <code class="literal">\\s*,\\s*</code>, which matches a comma surrounded by any number of contiguous spaces (possibly zero). This reduced our text to three nice, tidy fields.</p><p>With the new <code class="literal">Scanner</code> API, we could go a step further and parse the numbers of our second example as we extract them:</p><a id="I_10_tt598"/><pre class="programlisting"> <code class="n">String</code> <code class="n">text</code> <code class="o">=</code> <code class="s">"4231, Java Programming, 1000.00"</code><code class="o">;</code> <code class="n">Scanner</code> <code class="n">scanner</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code> <code class="n">text</code> <code class="o">).</code><code class="na">useDelimiter</code><code class="o">(</code><code class="s">"\\s*,\\s*"</code><code class="o">);</code> <code class="kt">int</code> <code class="n">checkNumber</code> <code class="o">=</code> <code class="n">scanner</code><code class="o">.</code><code class="na">nextInt</code><code class="o">();</code> <code class="c1">// 4231</code> <code class="n">String</code> <code class="n">description</code> <code class="o">=</code> <code class="n">scanner</code><code class="o">.</code><code class="na">next</code><code class="o">();</code> <code class="c1">// "Java Programming"</code> <code class="kt">float</code> <code class="n">amount</code> <code class="o">=</code> <code class="n">scanner</code><code class="o">.</code><code class="na">nextFloat</code><code class="o">();</code> <code class="c1">// 1000.00</code></pre><p>Here, we’ve told the <a id="I_indexterm10_id728388" class="indexterm"/><code class="literal">Scanner</code> to use our regular expression as the delimiter and then called it repeatedly to parse each field as its corresponding type. The <code class="literal">Scanner</code> is convenient because it can read not only from <code class="literal">String</code>s but directly from stream sources, such as <code class="literal">InputStream</code>s, <code class="literal">File</code>s, and <code class="literal">Channel</code>s:</p><a id="I_10_tt599"/><pre class="programlisting"> <code class="n">Scanner</code> <code class="n">fileScanner</code> <code class="o">=</code> <code class="k">new</code> <code class="n">Scanner</code><code class="o">(</code> <code class="k">new</code> <code class="n">File</code><code class="o">(</code><code class="s">"spreadsheet.csv"</code><code class="o">)</code> <code class="o">);</code> <code class="n">fileScanner</code><code class="o">.</code><code class="na">useDelimiter</code><code class="o">(</code> <code class="err">"\\</code><code class="n">s</code><code class="o">*,</code><code class="err">\\</code><code class="n">s</code><code class="o">*</code> <code class="o">);</code> <code class="c1">// ...</code></pre><p>Another thing that you can do with the <code class="literal">Scanner</code> is to look ahead with the “hasNext” methods to see if another item is coming:</p><a id="I_10_tt600"/><pre class="programlisting"> <code class="k">while</code><code class="o">(</code> <code class="n">scanner</code><code class="o">.</code><code class="na">hasNextInt</code><code class="o">()</code> <code class="o">)</code> <code class="o">{</code> <code class="kt">int</code> <code class="n">n</code> <code class="o">=</code> <code class="n">scanner</code><code class="o">.</code><code class="na">nextInt</code><code class="o">();</code> <code class="o">...</code> <code class="o">}</code></pre><div class="sect3" title="StringTokenizer"><div class="titlepage"><div><div><h3 class="title"><a id="learnjava3-CHP-10-SECT-4.2.1"/>StringTokenizer</h3></div></div></div><p><a id="idx10576" class="indexterm"/>Even though the <code class="literal">StringTokenizer</code> class that we mentioned is now a legacy item, it’s good to know that it’s there because it’s been around since the beginning of Java and is used in a lot of code. <code class="literal">StringTokenizer</code> allows you to specify a delimiter as a set of characters and matches any number or combination of those characters as a delimiter between tokens. The following snippet reads the words of our first example:</p><a id="I_10_tt601"/><pre class="programlisting"> <code class="n">String</code> <code class="n">text</code> <code class="o">=</code> <code class="s">"Now is the time for all good men (and women)..."</code><code class="o">;</code> <code class="n">StringTokenizer</code> <code class="n">st</code> <code class="o">=</code> <code class="k">new</code> <code class="n">StringTokenizer</code><code class="o">(</code> <code class="n">text</code> <code class="o">);</code> <code class="k">while</code> <code class="o">(</code> <code class="n">st</code><code class="o">.</code><code class="na">hasMoreTokens</code><code class="o">()</code> <code class="o">)</code> <code class="o">{</code> <code class="n">String</code> <code class="n">word</code> <code class="o">=</code> <code class="n">st</code><code class="o">.</code><code class="na">nextToken</code><code class="o">();</code> <code class="o">...</code> <code class="o">}</code></pre><p>We invoke the <a id="I_indexterm10_id728501" class="indexterm"/><code class="literal">hasMoreTokens()</code> and <a id="I_indexterm10_id728512" class="indexterm"/><code class="literal">nextToken()</code> methods to loop over the words of the text. By default, the <code class="literal">StringTokenizer</code> class uses standard whitespace characters—carriage return, newline, and tab—as delimiters. You can also specify your own set of delimiter characters in the <code class="literal">StringTokenizer</code> constructor. Any contiguous combination of the specified characters that appears in the target string is skipped between tokens:</p><a id="I_10_tt602"/><pre class="programlisting"> <code class="n">String</code> <code class="n">text</code> <code class="o">=</code> <code class="s">"4231, Java Programming, 1000.00"</code><code class="o">;</code> <code class="n">StringTokenizer</code> <code class="n">st</code> <code class="o">=</code> <code class="k">new</code> <code class="n">StringTokenizer</code><code class="o">(</code> <code class="n">text</code><code class="o">,</code> <code class="s">","</code> <code class="o">);</code> <code class="k">while</code> <code class="o">(</code> <code class="n">st</code><code class="o">.</code><code class="na">hasMoreTokens</code><code class="o">()</code> <code class="o">)</code> <code class="o">{</code> <code class="n">String</code> <code class="n">word</code> <code class="o">=</code> <code class="n">st</code><code class="o">.</code><code class="na">nextToken</code><code class="o">();</code> <code class="c1">// word = "4231", " Java Programming", "1000.00"</code> <code class="o">}</code></pre><p>This isn’t as clean as our regular expression example. Here we used a comma as the delimiter so we get extra leading whitespace in our description field. If we had added space to our delimiter string, the <code class="literal">StringTokenizer</code> would have broken our description into two words, “Java” and “Programming,” which is not what we wanted. A solution here would be to use <code class="literal">trim()</code> to remove the leading and trailing space on each element.<a id="I_indexterm10_id728569" class="indexterm"/><a id="I_indexterm10_id728576" class="indexterm"/><a id="I_indexterm10_id728583" class="indexterm"/><a id="I_indexterm10_id728590" class="indexterm"/><a id="I_indexterm10_id728597" class="indexterm"/><a id="I_indexterm10_id728604" class="indexterm"/></p></div></div></div></body></html>