使用 XML 与 Java NIO 的较量 如何转移话题至缓冲区和通道(1)
 
- UID
- 1066743
|

使用 XML 与 Java NIO 的较量 如何转移话题至缓冲区和通道(1)
用于那个站点的其中一个文档是一张参与者列表。由于那方面的内容已不属于本专栏的范围,所以用电子邮件程序将该列表作为通讯录来维护。令我头疼的是,这个文件的格式不是 XML。所以,我如何把它用于 XML 发布解决方案?我需要将这张列表转换成 XML。
编写一个专门的转换例程当然不困难,但这看起来象在浪费时间:因为还有其它需要把非-XML 文档用于 XML 解决方案的情况。除了通讯录,我可能还需要处理议程、电子表格、目录信息以及其它旧数据。
针对这个问题,XI 提供了一种普遍适用的解决方案。它使用正则表达式(现在已内置于 JDK 1.4 中)来解析输入文件并创建一个 XML 副本。我的上一篇专栏文章介绍了这些转换的详细信息以及 XI 的简短分析(请参阅 )。
JDK 1.4 中的新特性我决定现在开发 XI 的原因之一是想要用已构建到 JDK 1.4 中的新的正则表达式( regex )引擎进行实验。正如您将看到的,我所获得的远远超出了我在开始探究新 I/O 包(通常称为 NIO,它在包 java.nio 中)时所期待的。
包 java.regex该正则表达式引擎有一个简单且清晰的接口,它主要要求您了解两个新类和一个新接口。这两个新类是 Pattern 和 Matcher ,而新接口是 CharSequence 。(后者可能会产生问题,我将在后面讨论它。) 说明了如何使用 Pattern 和 Matcher 。
清单 1. 使用正则表达式引擎1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| import java.util.regex.*;
public class SampleRegex
{
public static void main(String[] params)
{
Pattern pattern =
Pattern.compile("(.*) .*)")
;
Matcher matcher =
pattern.matcher(params[0])
;
if(
matcher.matches()
)
{
System.out.print("Key:");
System.out.println(
matcher.group(1)
);
System.out.print("Value:");
System.out.println(matcher.group(2));
}
else
System.out.print("No match");
}
}
|
Pattern 是一个正则表达式编译器。它接受一个正则表达式并将它编译成 Matcher 。 Matcher 用于将正则表达式应用到字符串中,或者更确切地说,是应用到 CharSequence 中。
Pattern 没有公共的构造函数。要创建 Pattern ,必须调用它的 compile() ,同时传递一个正则表达式作为参数。
正则表达式 101正则表达式描述字符串的格式:使用最简单的正则表达式格式,您可以输入您希望匹配的文本。例如,正则表达式 ABC 将与字符串 ABC 匹配,但不与 DEF 匹配。
当然,正则表达式被限定为严格的字符串比较。例如,可以使用称为 百搭(joker) ― 一个句点( . )― 的通配符,它可以与一行中除了最后一个字符以外的任何一个字符相匹配。所以,正则表达式 A.C 将与字符串 ABC 、 AAC 、 AKC 以及许多其它字符串相匹配,但它依然不与 DEF 相匹配。
字符或百搭后面的星号( * )表明它可以与多个任意字符相匹配。因此,正则表达式 A*B 将与字符串 AB 、 AAB 、 AAAB 或以 A 开头并以单个 B 为结尾的任何其它字符串相匹配。
由于句点是一个百搭,所以 .* 将与包括 ABC 、 IBM developerWorks 和 DEF 在内的任何字符串相匹配。
圆括号用作分组操作符。它特别方便,因为正如您将看到的,从字符串中提取出一个组的内容是可能的。例如, 中使用的正则表达式 (.*) .*) 与由冒号分隔开的两个字符串相匹配。
有关正则表达式还有许多内容,我推荐您参考象 Mastering Regular Expressions那样的参考书(请参阅 )。
Pattern 和 Matcher在 中,一旦将正则表达式编译成 Pattern ,它就创建了带 matcher() 方法的 Matcher 。 Matcher 接受 CharSequence (在下一节 中将对此作更详细的讨论)并报告正则表达式是否匹配。 Matcher 提供了几个方法来测试正则表达式: matches() 、 lookingAt() 和 find() 。每个方法都可以应用正则表达式,但方式不同。
Matcher 还提供了 group() 来检索与给定组相匹配的字符串。从 1 到 n 对组进行编号,而 group(0) 是一个完整的正则表达式。
将正则表达式应用到命令行参数中。它打印找到的组(如果找到的话)。例如,用下面这条命令调用此应用程序:
1
| java SampleRegex "domain:ananas.org"
|
这时,它将打印:
1
2
| Key: domain
Value: ananas.org
|
因为输入( domain:ananas.org )与该正则表达式相匹配。然而,如果用下面这条命令调用此应用程序:
1
| java SampleRegex "ananas.org"
|
它将打印:
因为输入不与正则表达式相匹配。
NIO我在前一节 中提到过 CharSequence 。这是定义在 java.lang 包中用于字符数组的新接口。对 String 已进行了更新以便实现 CharSequence 。
更重要的是,通过使用 NIO 包,将一个文件作为 CharSequence 进行访问是可能的。因此,由于 Matcher 接受 CharSequence ,所以将正则表达式应用到全体文件是可能的。这就是我研究完 java.nio 包后得出的结论(请参阅参考资料)。
最终,在这个项目中我没有使用 java.nio,但我仍然要讨论它,因为我在寻找解决方案时花了许多时间。(在软件开发中,钻牛角尖是一项非常有趣的消遣,如果我不对此进行报告,那么本专栏本身就不真实。另外,我希望我的经历将为您节省一些时间以避免进行相同的研究。)
向您演示了如何将文件转换成 CharSequence 。实际上,您最终可以使用 CharBuffer 这个新类,它对文本文件实现 CharSequence 。
清单 2. 使用 CharBuffer1
2
3
4
5
6
7
8
9
| FileInputStream input = new FileInputStream(params[0]);
FileChannel channel = input.getChannel();
int fileLength = (int)channel.size();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY,0,fileLength);
Charset charset = Charset.forName("ISO-8859-1");
CharsetDecoder decoder = charset.newDecoder();
CharBuffer charBuffer = decoder.decode(buffer);
Matcher matcher = pattern.matcher(charBuffer);
// ...
|
在进一步讨论之前,我必须承认我不能确定我已经充分掌握了 NIO 背后的逻辑。我只是在最近才开始使用这个 API,这里只是我所掌握的。
正如您看到的,将文件转换成 CharBuffer 需要许多对象。这就是使用新 API 时的模式。就我所理解,NIO 的目的似乎是为了给您比常规 I/O 更多的控制和更大的灵活性。
NIO 没有太多抽象的 API。例如,使用 Java IO,不必担心缓冲区管理,但您也不能对它进行控制。而 NIO 向您提供了对缓冲区管理的更多控制 ― 通过让您运行它!可以论证的是,它更有效,但它也更复杂。
当我在写这篇专栏文章时,我对 NIO 的印象是,它将对开发诸如数据库引擎、服务器和高性能客户机的高性能应用程序的人员特别有用。而对于常规编程,我认为没有必要使用 NIO(因为这要花额外的精力)。
而且,我使用 XI 的最终目标是使它与 XMLReader 保持兼容,因为它是由 SAX 定义的。 XMLReader 通常使用 InputStream 或 Reader ,但不使用 NIO。我没有找到一种完全普遍使用的解决方案来将任何 InputStream 转换成 CharBuffer 。(但我确实找到了部分解决方案。)如果您有更好的想法,请给我发电子邮件,我将在下一篇专栏文章中报告它。
最后,我决定使用常规 I/O,将文件逐行地读取成字符串,并将正则表达式应用到那些字符串中。 |
|
|
|
|
|