??xml version="1.0" encoding="utf-8" standalone="yes"?>亚洲人精品亚洲人成在线,亚洲伊人色一综合网,中文字幕亚洲一区二区三区http://www.tkk7.com/iNeo/category/5351.htmlzh-cnWed, 28 Feb 2007 05:21:07 GMTWed, 28 Feb 2007 05:21:07 GMT60螌捕蝉、黄雀在后——从一个成语谈观察家模式[转]http://www.tkk7.com/iNeo/archive/2005/12/09/23165.html只牵q只?/dc:creator>只牵q只?/author>Fri, 09 Dec 2005 08:26:00 GMThttp://www.tkk7.com/iNeo/archive/2005/12/09/23165.htmlhttp://www.tkk7.com/iNeo/comments/23165.htmlhttp://www.tkk7.com/iNeo/archive/2005/12/09/23165.html#Feedback0http://www.tkk7.com/iNeo/comments/commentRss/23165.htmlhttp://www.tkk7.com/iNeo/services/trackbacks/23165.html阅读全文

]]>
Java Map 集合cȝ介[转]http://www.tkk7.com/iNeo/archive/2005/12/06/22703.html只牵q只?/dc:creator>只牵q只?/author>Tue, 06 Dec 2005 05:16:00 GMThttp://www.tkk7.com/iNeo/archive/2005/12/06/22703.htmlhttp://www.tkk7.com/iNeo/comments/22703.htmlhttp://www.tkk7.com/iNeo/archive/2005/12/06/22703.html#Feedback0http://www.tkk7.com/iNeo/comments/commentRss/22703.htmlhttp://www.tkk7.com/iNeo/services/trackbacks/22703.html
java.util 中的集合cd?Java 中某些最常用的类?最常用的集合类?List ?Map?List 的具体实现包?ArrayList ?VectorQ它们是可变大小的列表,比较适合构徏、存储和操作Mcd对象的元素列表?List 适用于按数值烦引访问元素的情Ş?

Map 提供了一个更通用的元素存储方法?Map 集合cȝ于存储元素对Q称作“键”和“值”)Q其中每个键映射C个倹{?从概念上而言Q您可以?List 看作是具有数值键?Map?而实际上Q除?List ?Map 都在定义 java.util 中外Q两者ƈ没有直接的联pR本文将着重介l核?Java 发行套g中附带的 MapQ同时还介l如何采用或实现更适用于您应用E序特定数据的专?Map?

了解 Map 接口和方?

Java 核心cM有很多预定义?Map cR?在介l具体实C前,我们先介l一?Map 接口本nQ以便了解所有实现的共同炏V?Map 接口定义了四U类型的ҎQ每?Map 都包含这些方法?下面Q我们从两个普通的ҎQ表 1Q开始对q些Ҏ加以介绍?

?1Q?覆盖的方法?我们这 Object 的这两个Ҏ覆盖Q以正确比较 Map 对象的等h?equals(Object o) 比较指定对象与此 Map 的等h?
hashCode() q回?Map 的哈希码



Map 构徏

Map 定义了几个用于插入和删除元素的变换方法(?2Q?

?2Q?Map 更新ҎQ?可以更改 Map 内容?clear() ?Map 中删除所有映?
remove(Object key) ?Map 中删除键和关联的?
put(Object key, Object value) 指定g指定键相兌
clear() ?Map 中删除所有映?
putAll(Map t) 指?Map 中的所有映复制到?map



管您可能注意到Q纵然假讑ֿ略构Z个需要传递给 putAll() ?Map 的开销Q?putAll() 通常也ƈ不比使用大量?put() 调用更有效率Q但 putAll() 的存在一点也不稀奇?q是因ؓQputAll() 除了q代 put() 所执行的将每个键值对d?Map 的算法以外,q需要P代所传递的 Map 的元素?但应注意QputAll() 在添加所有元素之前可以正调?Map 的大,因此如果您未亲自调整 Map 的大(我们对此进行简单介l)Q则 putAll() 可能比预期的更有效?

查看 Map

q代 Map 中的元素不存在直接了当的Ҏ?如果要查询某?Map 以了解其哪些元素满特定查询Q或如果要P代其所有元素(无论原因如何Q,则您首先需要获取该 Map 的“视䏀?有三U可能的视图Q参见表 3Q?

所有键值对 ?参见 entrySet()
所有键 ?参见 keySet()
所有??参见 values()

前两个视囑֝q回 Set 对象Q第三个视图q回 Collection 对象?p两种情况而言Q问题到q里q没有结束,q是因ؓ您无法直接P?Collection 对象?Set 对象。要q行q代Q您必须获得一?Iterator 对象?因此Q要q代 Map 的元素,必须q行比较烦琐的编?


Iterator keyValuePairs = aMap.entrySet().iterator();
Iterator keys = aMap.keySet().iterator();
Iterator values = aMap.values().iterator();


值得注意的是Q这些对象(Set、Collection ?IteratorQ实际上是基 Map 的视图,而不是包含所有元素的副本?q它们的用效率很高?另一斚wQCollection ?Set 对象?toArray() Ҏ却创建包?Map 所有元素的数组对象Q因此除了确实需要用数l中元素的情形外Q其效率q不高?

我运行了一个小试Q随附文件中?Test1Q,该测试用了 HashMapQƈ使用以下两种Ҏ对P?Map 元素的开销q行了比较:


int mapsize = aMap.size();

Iterator keyValuePairs1 = aMap.entrySet().iterator();
for (int i = 0; i < mapsize; i++)
{
Map.Entry entry = (Map.Entry) keyValuePairs1.next();
Object key = entry.getKey();
Object value = entry.getValue();
...
}

Object[] keyValuePairs2 = aMap.entrySet().toArray();
for (int i = 0; i < rem; i++) {
{
Map.Entry entry = (Map.Entry) keyValuePairs2[i];
Object key = entry.getKey();


Object value = entry.getValue();
...
}


此测试用了两种量ҎQ?一U是量q代元素的时_另一U测量?toArray 调用创徏数组的其他开销?W一U方法(忽略创徏数组所需的时_表明Q用已?toArray 调用中创建的数组q代元素的速度要比使用 Iterator 的速度大约?30%-60%?但如果将使用 toArray Ҏ创徏数组的开销包含在内Q则使用 Iterator 实际上要?10%-20%?因此Q如果由于某U原因要创徏一个集合元素的数组而非q代q些元素Q则应用该数组q代元素?但如果您不需要此中间数组Q则不要创徏它,而是使用 Iterator q代元素?

?3Q?q回视图?Map ҎQ?使用q些Ҏq回的对象,您可以遍?Map 的元素,q可以删?Map 中的元素?entrySet() q回 Map 中所包含映射?Set 视图?Set 中的每个元素都是一?Map.Entry 对象Q可以?getKey() ?getValue() ҎQ还有一?setValue() ҎQ访问后者的键元素和值元?
keySet() q回 Map 中所包含键的 Set 视图?删除 Set 中的元素q将删除 Map 中相应的映射Q键和|
values() q回 map 中所包含值的 Collection 视图?删除 Collection 中的元素q将删除 Map 中相应的映射Q键和|



讉K元素

?4 中列Z Map 讉KҎ。Map 通常适合按键Q而非按|q行讉K?Map 定义中没有规定这肯定是真的,但通常您可以期望这是真的?例如Q您可以期望 containsKey() Ҏ?get() Ҏ一样快?另一斚wQcontainsValue() Ҏ很可能需要扫?Map 中的|因此它的速度可能比较慢?

?4Q?Map 讉K和测试方法: q些Ҏ索有?Map 内容的信息但不更?Map 内容?get(Object key) q回与指定键兌的?
containsKey(Object key) 如果 Map 包含指定键的映射Q则q回 true
containsValue(Object value) 如果?Map 一个或多个键映到指定|则返?true
isEmpty() 如果 Map 不包含键-值映,则返?true
size() q回 Map 中的?值映的数目



对?containsKey() ?containsValue() 遍历 HashMap 中所有元素所需旉的测试表明,containsValue() 所需的时间要长很多?实际上要长几个数量Q?Q参见图 1 和图 2Q以及随附文件中?Test2Q?因此Q如?containsValue() 是应用程序中的性能问题Q它很快显现出来,q可以通过监测您的应用E序L地将其识别?q种情况下,我相信您能够惛_一个有效的替换Ҏ来实?containsValue() 提供的等效功能?但如果想不出办法Q则一个可行的解决Ҏ是再创徏一?MapQƈ第一?Map 的所有g为键?q样Q第一?Map 上的 containsValue() 成为第二个 Map 上更有效?containsKey()?


?1Q?使用 JDeveloper 创徏q运?Map 试c?




?2Q??JDeveloper 中用执行监器q行的性能监测查出应用E序中的瓉



核心 Map

Java 自带了各U?Map cR?q些 Map cd归ؓ三种cdQ?


通用 MapQ用于在应用E序中管理映,通常?java.util E序包中实现
HashMap
Hashtable
Properties
LinkedHashMap
IdentityHashMap
TreeMap
WeakHashMap
ConcurrentHashMap
专用 MapQ您通常不必亲自创徏此类 MapQ而是通过某些其他cd其进行访?
java.util.jar.Attributes
javax.print.attribute.standard.PrinterStateReasons
java.security.Provider
java.awt.RenderingHints
javax.swing.UIDefaults
一个用于帮助实现您自己?Map cȝ抽象c?
AbstractMap

内部哈希Q?哈希映射技?

几乎所有通用 Map 都用哈希映?q是一U将元素映射到数l的非常单的机制Q您应了解哈希映的工作原理Q以便充分利?Map?

哈希映射l构׃个存储元素的内部数组l成?׃内部采用数组存储Q因此必然存在一个用于确定Q意键讉K数组的烦引机制?实际上,该机刉要提供一个小于数l大的整数索引倹{?该机制称作哈希函数??Java Z哈希?Map 中,哈希函数对象{换ؓ一个适合内部数组的整数?您不必ؓL一个易于用的哈希函数而大伤脑{: 每个对象都包含一个返回整数值的 hashCode() Ҏ?要将该值映到数组Q只需其转换Z个正|然后在将该值除以数l大后取余数即可?以下是一个简单的、适用于Q何对象的 Java 哈希函数


int hashvalue = Maths.abs(key.hashCode()) % table.length;


Q? 二进制运符Q称作模Q将左侧的值除以右侧的|然后q回整数形式的余数。)

实际上,?1.4 版发布之前,q就是各U基于哈希的 Map cL使用的哈希函数?但如果您查看一下代码,您将看到


int hashvalue = (key.hashCode() & 0x7FFFFFFF) % table.length;


它实际上是用更快机制获取正值的同一函数??1.4 版中QHashMap cdC用一个不同且更复杂的哈希函数Q该函数Z Doug Lea ?util.concurrent E序包(E后我将更详l地再次介绍 Doug Lea 的类Q?


?3Q?哈希工作原理



该图介绍了哈希映的基本原理Q但我们q没有对其进行详l介l?我们的哈希函数将L对象映射C个数l位|,但如果两个不同的键映到相同的位|,情况会如何Q?q是一U必然发生的情况?在哈希映的术语中,q称作冲H?Map 处理q些冲突的方法是在烦引位|处插入一个链接列表,q简单地元素添加到此链接列表?因此Q一个基于哈希的 Map 的基?put() Ҏ可能如下所C?


public Object put(Object key, Object value) {
//我们的内部数l是一?Entry 对象数组
//Entry[] table;

//获取哈希码,q映到一个烦?
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % table.length;

//循环遍历位于 table[index] 处的链接列表Q以查明
//我们是否拥有此键??如果拥有Q则覆盖?
for (Entry e = table[index] ; e != null ; e = e.next) {
//必须查键是否相等Q原因是不同的键对象
//可能拥有相同的哈?
if ((e.hash == hash) && e.key.equals(key)) {
//q是相同键,覆盖该?
//q从该方法返?old ?
Object old = e.value;
e.value = value;
return old;
}
}

//仍然在此处,因此它是一个新键,只需d一个新 Entry
//Entry 对象包含 key 对象?value 对象、一个整型的 hash?
//和一个指向列表中的下一?Entry ?next Entry

//创徏一个指向上一个列表开头的?EntryQ?
//q将此新 Entry 插入表中
Entry e = new Entry(hash, key, value, table[index]);
table[index] = e;

return null;
}



如果看一下各U基于哈希的 Map 的源代码Q您发现这基本上就是它们的工作原理?此外Q还有一些需要进一步考虑的事,如处理空键和g及调整内部数l?此处定义?put() Ҏq包含相?get() 的算法,q是因ؓ插入包括搜烦映射索引处的以查明该键是否已经存在?Q即 get() Ҏ?put() Ҏh相同的算法,?get() 不包含插入和覆盖代码。) 使用链接列表q不是解军_H的唯一ҎQ某些哈希映用另一U“开攑ּd”方案,本文对其不予介绍?

优化 Hasmap

如果哈希映射的内部数l只包含一个元素,则所有项映到此数l位|,从而构成一个较长的链接列表?׃我们的更新和讉K使用了对链接列表的线性搜索,而这要比 Map 中的每个数组索引只包含一个对象的情Ş要慢得多Q因此这样做的效率很低?讉K或更新链接列表的旉与列表的大小U性相养I而用哈希函数访问或更新数组中的单个元素则与数组大小无关 ?渐q性质QBig-O 表示法)而言Q前者ؓ O(n)Q而后者ؓ O(1)?因此Q用一个较大的数组而不是让太多的项聚集在太的数组位置中是有意义的?

调整 Map 实现的大?

在哈希术语中Q内部数l中的每个位|称作“存储桶?bucket)Q而可用的存储桶数Q即内部数组的大)UC定w (capacity)?Z Map 对象有效地处理Q意数目的,Map 实现可以调整自n的大?但调整大的开销很大?调整大小需要将所有元素重新插入到新数l中Q这是因Z同的数组大小意味着对象现在映射C同的索引倹{?先前冲突的键可能不再冲突Q而先前不冲突的其他键现在可能冲突?q显然表明,如果?Map 调整得够大Q则可以减少甚至不再需要重新调整大,q很有可能显著提高速度?

使用 1.4.2 JVM q行一个简单的试Q即用大量的(数目过一百万Q填?HashMap??5 昄了结果,q将所有时间标准化为已预先讄大小的服务器模式Q关联文件中?Test3Q?对于已预先设|大的 JVMQ客L和服务器模式 JVM q行旉几乎相同Q在攑ּ JIT ~译阶段后)?但?Map 的默认大将引发多次调整大小操作Q开销很大Q在服务器模式下要多?50% 的时_而在客户端模式下几乎要多用两倍的旉Q?

?5Q?填充已预先设|大的 HashMap 与填充默认大的 HashMap 所需旉的比?客户端模?服务器模?
预先讄的大?100% 100%
默认大小 294% 157%



使用负蝲因子

为确定何时调整大,而不是对每个存储桶中的链接列表的深度q行记数Q基于哈希的 Map 使用一个额外参数ƈ_略计算存储桶的密度?Map 在调整大之前,使用名ؓ“负载因子”的参数指示 Map 承担的“负载”量Q即它的负蝲E度?负蝲因子、项敎ͼMap 大小Q与定w之间的关pȝ单明了:


如果Q负载因子)xQ容量)>QMap 大小Q,则调?Map 大小

例如Q如果默认负载因子ؓ 0.75Q默认容量ؓ 11Q则 11 x 0.75 = 8.25Q该值向下取整ؓ 8 个元素?因此Q如果将W?8 个项d到此 MapQ则?Map 自w的大小调整Z个更大的倹{?相反Q要计算避免调整大小所需的初始容量,用将要添加的Ҏ除以负蝲因子Qƈ向上取整Q例如,


对于负蝲因子?0.75 ?100 个项Q应容量设|ؓ 100/0.75 = 133.33Qƈ结果向上取整ؓ 134Q或取整?135 以用奇敎ͼ

奇数个存储桶?map 能够通过减少冲突数来提高执行效率?虽然我所做的试Q关联文件中的Test4Qƈ未表明质数可以始l获得更好的效率Q但理想情Ş是容量取质数?1.4 版后的某?MapQ如 HashMap ?LinkedHashMapQ而非 Hashtable ?IdentityHashMapQ用需?2 的幂定w的哈希函敎ͼ但下一个最?2 的幂定wp?Map 计算Q因此您不必亲自计算?

负蝲因子本n是空间和旉之间的调整折街?较小的负载因子将占用更多的空_但将降低冲突的可能性,从而将加快讉K和更新的速度?使用大于 0.75 的负载因子可能是不明智的Q而用大?1.0 的负载因子肯定是不明知的Q这是因必定会引发一ơ冲H?使用于 0.50 的负载因子好处ƈ不大Q但只要您有效地调整 Map 的大,应不会对负载因子造成性能开销Q而只会造成内存开销?但较的负蝲因子意味着如果您未预先调整 Map 的大,则导致更频繁的调整大,从而降低性能Q因此在调整负蝲因子时一定要注意q个问题?

选择适当?Map

应用哪U?MapQ?它是否需要同步? 要获得应用程序的最x能Q这可能是所面的两个最重要的问题?当用通用 Map Ӟ调整 Map 大小和选择负蝲因子늛?Map 调整选项?

以下是一个用于获得最?Map 性能的简单方?

您的所?Map 变量声明?MapQ而不是Q何具体实玎ͼ即不要声明ؓ HashMap ?HashtableQ或M其他 Map cd现?


Map criticalMap = new HashMap(); //?

HashMap criticalMap = new HashMap(); //?


q您能够只更改一行代码即可非常轻村֜替换M特定?Map 实例?

下蝲 Doug Lea ?util.concurrent E序?(http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html)??ConcurrentHashMap 用作默认 Map?当移植到 1.5 版时Q将 java.util.concurrent.ConcurrentHashMap 用作您的默认 Map?不要?ConcurrentHashMap 包装在同步的包装器中Q即使它用于多个线E?使用默认大小和负载因子?
监测您的应用E序?如果发现某个 Map 造成瓉Q则分析造成瓉的原因,q分或全部更改?Map 的以下内容: Map c;Map 大小Q负载因子;关键对象 equals() Ҏ实现?专用?Map 的基本上都需要特D用途的定制 Map 实现Q否则通用 Map 实现您所需的性能目标?

Map 选择

也许您曾期望更复杂的考量Q而这实际上是否显得太ҎQ?好的Q让我们慢慢来?首先Q您应用哪U?MapQ?{案很简单: 不要为您的设计选择M特定?MapQ除非实际的设计需要指定一个特D类型的 Map?设计旉常不需要选择具体?Map 实现?您可能知道自己需要一?MapQ但不知道用哪U?而这恰恰是使用 Map 接口的意义所在?直到需要时再选择 Map 实现 ?如果随处使用“Map”声明的变量Q则更改应用E序中Q何特D?Map ?Map 实现只需要更改一行,q是一U开销很少的调整选择?是否要用默认的 Map 实现Q?我很快将谈到q个问题?

同步 Map

同步与否有何差别Q?Q对于同步,您既可以使用同步?MapQ也可以使用 Collections.synchronizedMap() 未同步?Map 转换为同步的 Map?后者用“同步的包装器”)q是一个异常复杂的选择Q完全取决于您如何根据多U程q发讉K和更C?MapQ同时还需要进行维护方面的考虑?例如Q如果您开始时未ƈ发更新特?MapQ但它后来更改ؓq发更新Q情况将如何Q?在这U情况下Q很Ҏ在开始时使用一个未同步?MapQƈ在后来向应用E序中添加ƈ发更新线E时忘记此未同步的 Map 更改为同步的 Map?q将使您的应用程序容易崩溃(一U要定和跟t的最p糕的错误)?但如果默认ؓ同步Q则因随之而来的可怕性能而序列化执行多线E应用程序?看v来,我们需要某U决{树来帮助我们正选择?

Doug Lea 是纽U州立大学奥斯威戈分校计机U学pȝ教授?他创Z一l公共领域的E序包(l称 util.concurrentQ,该程序包包含许多可以化高性能q行~程的实用程序类?q些cM包含两个 MapQ即 ConcurrentReaderHashMap ?ConcurrentHashMap?q些 Map 实现是线E安全的Qƈ且不需要对q发讉K或更新进行同步,同时q适用于大多数需?Map 的情c?它们q远比同步的 MapQ如 HashtableQ或使用同步的包装器更具伸羃性,q且?HashMap 相比Q它们对性能的破坏很?util.concurrent E序包构成了 JSR166 的基QJSR166 已经开发了一个包含在 Java 1.5 版中的ƈ发实用程序,?Java 1.5 版将把这?Map 包含在一个新?java.util.concurrent E序包中?

所有这一切意味着您不需要一个决{树来决定是使用同步?Map q是使用非同步的 MapQ?而只需使用 ConcurrentHashMap?当然Q在某些情况下,使用 ConcurrentHashMap q不合适?但这些情况很见Qƈ且应具体情况具体处理?q就是监的用途?


l束?

通过 Oracle JDeveloper 可以非常L地创Z个用于比较各U?Map 性能的测试类?更重要的是,集成良好的监器可以在开发过E中快速、轻村֜识别性能瓉 - 集成?IDE 中的监测器通常被较频繁C用,以便帮助构徏一个成功的工程?现在Q您已经拥有了一个监器q了解了有关通用 Map 及其性能的基知识Q可以开始运行您自己的测试,以查明您的应用程序是否因 Map 而存在瓶颈以及在何处需要更Ҏ使用?Map?

以上内容介绍了通用 Map 及其性能的基知识?当然Q有关特?Map 实现以及如何Ҏ不同的需求用它们还存在更多复杂和值得x的事,q些在本文W?2 部分中介l?


--------------------------------------------------------------------------------
Jack Shirazi ?O''Reilly 的“Java 性能调整”的作者,以及受欢q的 JavaPerformanceTuning.com |站Q提?Java 性能信息的全球知名站点)的ȝ?Jack ?Java 性能领域提供咨询q著书立说?他还监督 JavaPerformanceTuning.com 提供的信息,其中包括每年大约发布 1000 条性能技巧以及许多有x能工具、讨论组{内容的文章?Jack 早年q曾发布有关蛋白质结构预以及黑z热力学斚w的文章,而且在其I闲时还Ҏ?Perl5 核心模块作出了A献?/FONT>



]]>
正则表达式之道[转]http://www.tkk7.com/iNeo/archive/2005/12/06/22655.html只牵q只?/dc:creator>只牵q只?/author>Tue, 06 Dec 2005 01:25:00 GMThttp://www.tkk7.com/iNeo/archive/2005/12/06/22655.htmlhttp://www.tkk7.com/iNeo/comments/22655.htmlhttp://www.tkk7.com/iNeo/archive/2005/12/06/22655.html#Feedback0http://www.tkk7.com/iNeo/comments/commentRss/22655.htmlhttp://www.tkk7.com/iNeo/services/trackbacks/22655.html原著QSteve Mansour
sman@scruznet.com
Revised: June 5, 1999
(copied by jm /at/ jmason.org from http://www.scruz.net/%7esman/regexp.htm, after the original disappeared! )

译QNeo Lee
什么是正则表达?/P>

一个正则表辑ּQ就是用某种模式d配一cdW串的一个公式。很多h因ؓ它们看上L较古怪而且复杂所以不敢去使用——很不幸Q这文章也不能够改变这一点,不过Q经q一点点l习之后我就开始觉得这些复杂的表达式其实写hq是相当单的Q而且Q一旦你弄懂它们Q你p把数时辛苦而且易错的文本处理工作压~在几分钟(甚至几秒钟)内完成。正则表辑ּ被各U文本编辑Y件、类库(例如Rogue Wave的tools.h++Q、脚本工P像awk/grep/sedQ广泛的支持Q而且像Microsoft的Visual C++q种交互式IDE也开始支持它了?

我们在如下的章节中利用一些例子来解释正则表达式的用法Q绝大部分的例子是基?B>vi中的文本替换命o?B>grep文g搜烦命o来书写的Q不q它们都是比较典型的例子Q其中的概念可以在sed、awk、perl和其他支持正则表辑ּ的编E语a中用。你可以看看不同工具中的正则表达?/A>q一节,其中有一些在别的工具中用正则表辑ּ的例子。还有一个关于vi中文本替换命令(sQ的单说?/A>附在文后供参考?/P>

正则表达式基

正则表达式由一些普通字W和一?I>元字W(metacharactersQ?/I>l成。普通字W包括大写的字母和数字Q而元字符则具有特D的含义Q我们下面会l予解释?

在最单的情况下,一个正则表辑ּ看上d是一个普通的查找丌Ӏ例如,正则表达?testing"中没有包含Q何元字符Q,它可以匹?testing"?123testing"{字W串Q但是不能匹?Testing"?/P>

要想真正的用好正则表辑ּQ正的理解元字W是最重要的事情。下表列Z所有的元字W和对它们的一个简短的描述?/P>

元字W?/I>   描述


.
匚wM单个字符。例如正则表辑ּr.t匚wq些字符Ԍrat?I>rut?I>r tQ但是不匚wroot?nbsp;
$
匚w行结束符。例如正则表辑ּweasel$ 能够匚w字符?He's a weasel"的末,但是不能匚w字符?They are a bunch of weasels."?nbsp;
^
匚w一行的开始。例如正则表辑ּ^When in能够匚w字符?When in the course of human events"的开始,但是不能匚w"What and When in the"?/I>
*
匚w0或多个正好在它之前的那个字符。例如正则表辑ּ.*意味着能够匚wL数量的Q何字W?/TD>
\
q是引用府,用来这里列出的q些元字W当作普通的字符来进行匹配。例如正则表辑ּ\$被用来匹配美元符P而不是行,cM的,正则表达?TT>\.用来匚w点字W,而不是Q何字W的通配W?/TD>
[ ] 
[c1-c2]
[^c1-c2]
匚w括号中的M一个字W。例如正则表辑ּr[aou]t匚wrat?I>rot?I>rutQ但是不匚wret。可以在括号中用连字符-来指定字W的区间Q例如正则表辑ּ[0-9]可以匚wM数字字符Q还可以制定多个区间Q例如正则表辑ּ[A-Za-z]可以匚wM大小写字母。另一个重要的用法是“排除”,要想匚w除了指定区间之外的字W——也是所谓的补集——在左边的括号和W一个字W之间用^字符Q例如正则表辑ּ[^269A-Z] 匹配除???和所有大写字母之外的M字符?/TD>
\< \>
匚w词(wordQ的开始(\<Q和l束Q\>Q。例如正则表辑ּ\<the能够匚w字符?for the wise"中的"the"Q但是不能匹配字W串"otherwise"中的"the"?STRONG>注意Q这个元字符不是所有的软g都支持的?/TD>
\( \)
?\( ?\) 之间的表辑ּ定义为“组”(groupQ,q且匹配这个表辑ּ的字W保存到一个时区域(一个正则表辑ּ中最多可以保?个)Q它们可以用 \1 ?B>\9 的符h引用?/TD>
|
两个匹配条件进行逻辑“或”(OrQ运。例如正则表辑ּ(him|her) 匚w"it belongs to him"?it belongs to her"Q但是不能匹?it belongs to them."?STRONG>注意Q这个元字符不是所有的软g都支持的?/TD>
+
匚w1或多个正好在它之前的那个字符。例如正则表辑ּ9+匚w9?9?99{?STRONG>注意Q这个元字符不是所有的软g都支持的?/TD>
?
匚w0?个正好在它之前的那个字符?STRONG>注意Q这个元字符不是所有的软g都支持的?/TD>
\{i\}
\{i,j\}
匚w指定数目的字W,q些字符是在它之前的表达式定义的。例如正则表辑ּA[0-9]\{3\} 能够匚w字符"A"后面跟着正好3个数字字W的Ԍ例如A123、A348{,但是不匹配A1234。而正则表辑ּ[0-9]\{4,6\} 匚wq箋的Q?个?个或?个数字字W?STRONG>注意Q这个元字符不是所有的软g都支持的?/TD>


最单的元字W是点,它能够匹配Q何单个字W(注意?/STRONG>包括新行W)。假定有个文件test.txt包含以下几行内容Q?/P>

    he is a rat
    he is in a rut
    the food is Rotten
    I like root beer

我们可以使用grep命o来测试我们的正则表达式,grep命o使用正则表达式去试匚w指定文g的每一行,q将臛_有一处匹配表辑ּ的所有行昄出来。命?

    grep r.t test.txt

在test.txt文g中的每一行中搜烦正则表达?B>r.tQƈ打印输出匚w的行。正则表辑ּr.t匚w一?B>r接着M一个字W再接着一?B>t。所以它匹配文件中?B>rat?B>rutQ而不能匹?B>Rotten中的RotQ因为正则表辑ּ是大写敏感的。要惛_时匹配大写和写字母Q应该用字W区间元字符Q方括号Q。正则表辑ּ[Rr]能够同时匚wR?B>r。所以,要想匚w一个大写或者小写的r接着M一个字W再接着一?B>tp使用q个表达式:[Rr].t?

要想匚w行首的字W要使用抑扬字符Q?EM>^Q——又是也被叫做插入符。例如,x到text.txt中行?he"打头的行Q你可能会先用简单表辑ּheQ但是这会匹配第三行?B>theQ所以要使用正则表达?B>^heQ它只匹配在行首出现?B>h?

有时候指定“除了×××都匚w”会比较Ҏ辑ֈ目的Q当抑扬字符Q?EM>^Q出现在Ҏ号中是,它表C“排除”,例如要匹?B>he Q但是排除前面是t or s的情性(也就?B>the?B>sheQ,可以使用Q?B>[^st]he?

可以使用Ҏh指定多个字符区间。例如正则表辑ּ[A-Za-z]匚wM字母Q包括大写和写的;正则表达?B>[A-Za-z][A-Za-z]* 匚w一个字母后面接着0或者多个字母(大写或者小写)。当然我们也可以用元字符+做到同样的事情,也就是:[A-Za-z]+ Q和[A-Za-z][A-Za-z]*完全{h。但是要注意元字W?B>+ q不是所有支持正则表辑ּ的程序都支持的。关于这一点可以参考后面的正则表达式语法支持情?/A>?/P>

要指定特定数量的匚wQ要使用大括P注意必须使用反斜杠来转义Q。想匚w所?B>100?B>1000的实例而排?B>10?B>10000Q可以用:10\{2,3\}Q这个正则表辑ּ匚w数字1后面跟着2或??的模式。在q个元字W的使用中一个有用的变化是忽略第二个数字Q例如正则表辑ּ0\{3,\} 匹配至?个连l的0?/P>

单的例子

q里有一些有代表性的、比较简单的例子?

vi 命o 作用


:%s/ */ /g 把一个或者多个空格替换ؓ一个空根{?/TD>
:%s/ *$// L行尾的所有空根{?/TD>
:%s/^/ / 在每一行头上加入一个空根{?/TD>
:%s/^[0-9][0-9]* // L行首的所有数字字W?/TD>
:%s/b[aeio]g/bug/g 所有的bag?I>beg?I>big?I>bog改ؓbug?nbsp;
:%s/t\([aou]\)g/h\1t/g 所?I>tag?I>tog?I>tug分别改ؓhat?I>hot?I>hugQ注意用group的用法和使用\1引用前面被匹配的字符Q?/TD>

中的例子(奇的咒语)

?

所有方法foo(a,b,c)的实例改为foo(b,a,c)。这里a、b和c可以是Q何提供给Ҏfoo()的参数。也是说我们要实现q样的{换:

之前   之后
foo(10,7,2) foo(7,10,2)
foo(x+13,y-2,10) foo(y-2,x+13,10)
foo( bar(8), x+y+z, 5) foo( x+y+z, bar(8), 5)

下面q条替换命o能够实现q一法Q?/P>

    :%s/foo(\([^,]*\),\([^,]*\),\([^)]*\))/foo(\2,\1,\3)/g

现在让我们把它打散来加以分析。写个表辑ּ的基本思\是找出foo()和它的括号中的三个参数的位置。第一个参数是用这个表辑ּ来识别的Q:\([^,]*\)Q我们可以从里向外来分析它: 

[^,]   除了逗号之外的Q何字W?/TD>
[^,]* 0或者多个非逗号字符
\([^,]*\) 这些非逗号字符标记?B>\1Q这样可以在之后的替换模式表辑ּ中引用它
\([^,]*\), 我们必须扑ֈ0或者多个非逗号字符后面跟着一个逗号Qƈ且非逗号字符那部分要标记出来以备后用?/TD>

现在正是指出一个用正则表辑ּ常见错误的最x机。ؓ什么我们要使用[^,]*q样的一个表辑ּQ而不是更加简单直接的写法Q例如:.*Q来匚wW一个参数呢Q设x们用模?B>.*来匹配字W串"10,7,2"Q它应该匚w"10,"q是"10,7,"Qؓ了解册个两义性(ambiguityQ,正则表达式规定一律按照最长的串来Q在上面的例子中是"10,7,"Q显然这样就扑և了两个参数而不是我们期望的一个。所以,我们要?B>[^,]*来强制取出第一个逗号之前的部分?/P>

q个表达式我们已l分析到了:foo(\([^,]*\)Q这一D可以简单的译为“当你找?B>foo(把其后直到W一个逗号之前的部分标Cؓ\1”。然后我们用同L办法标记W二个参Cؓ\2。对W三个参数的标记Ҏ也是一P只是我们要搜索所有的字符直到x受我们ƈ没有必要L索第三个参数Q因为我们不需要调整它的位|,但是q样的模式能够保证我们只L换那些有三个参数的foo()Ҏ调用Q在foo()是一个重载(overoadingQ方法时q种明确的模式往往是比较保险的。然后,在替换部分,我们扑ֈfoo()的对应实例,然后利用标记好的部分q行替换Q是的第一和第二个参数交换位置?/P>

?

假设有一个CSVQcomma separated valueQ文Ӟ里面有一些我们需要的信息Q但是格式却有问题,目前数据的列序是:姓名Q公司名Q州名羃写,邮政~码Q现在我们希望讲q些数据重新l织Q以便在我们的某个Y件中使用Q需要的格式为:姓名Q州名羃?邮政~码Q公司名。也是_我们要调整列序Q还要合q两个列来构成一个新列。另外,我们的Y件不能接受逗号前后面有MI格Q包括空格和制表W)所以我们还必须要去掉逗号前后的所有空根{?

q里有几行我们现在的数据Q?/P>

    Bill Jones,     HI-TEK Corporation ,  CA, 95011
    Sharon Lee Smith,  Design Works Incorporated,  CA, 95012
    B. Amos   ,  Hill Street Cafe,  CA, 95013
    Alexander Weatherworth,  The Crafts Store,  CA, 95014
    ...

我们希望把它变成q个样子Q?

    Bill Jones,CA 95011,HI-TEK Corporation
    Sharon Lee Smith,CA 95012,Design Works Incorporated
    B. Amos,CA 95013,Hill Street Cafe
    Alexander Weatherworth,CA 95014,The Crafts Store
    ...

我们用两个正则表达式来解决q个问题。第一个移动列和合q列Q第二个用来LI格?

下面是W一个替换命令:

    :%s/\([^,]*\),\([^,]*\),\([^,]*\),\(.*\)/\1,\3 \4,\2/

q里的方法跟?基本一PW一个列Q姓名)用这个表辑ּ来匹配:\([^,]*\)Q即W一个逗号之前的所有字W,而姓名内容被?B>\1标记下来。公司名和州名羃写字D는同样的方法标Cؓ\2?B>\3Q而最后一个字D는\(.*\)来匹配("匚w所有字W直到行?Q。替换部分则引用上面标记的那些内Ҏq行构造?

下面q个替换命o则用来去除空|

    :%s/[ \t]*,[ \t]*/,/g

我们q是分解来看Q?B>[ \t]匚wI格/制表W,[ \t]* 匚w0或多个空?制表W,[ \t]*,匚w0或多个空?制表W后面再加一个逗号Q最后,[ \t]*,[ \t]*匚w0或多个空?制表W接着一个逗号再接着0或多个空?制表W。在替换部分Q我们简单的我们扑ֈ的所有东西替换成一个逗号。这里我们用了l尾的可选的g参数Q这表示在每行中Ҏ有匹配的串执行替换(而不是缺省的只替换第一个匹配串Q?

?

假设有一个多字符的片断重复出玎ͼ例如Q?

Billy tried really hard
Sally tried really really hard
Timmy tried really really really hard
Johnny tried really really really really hard

而你x"really"?really really"Q以及Q意数量连l出现的"really"字符串换成一个简单的"very"Qsimple is good!Q,那么以下命oQ?

:%s/\(really \)\(really \)*/very /

׃把上q的文本变成Q?

Billy tried very hard
Sally tried very hard
Timmy tried very hard
Johnny tried very hard

表达?B>\(really \)*匚w0或多个连l的"really "Q注意结有个空|Q?B>\(really \)\(really \)* 匚w1个或多个q箋?really "实例?

困难的例子(不可思议的象形文字)

Coming soon.


不同工具中的正则表达?/H1>

OKQ你已经准备使用REQregular expressionsQ正则表辑ּQ,但是你ƈ准备使用vi。所以,在这里我们给Z些在其他工具中用RE的例子。另外,我还会ȝ一下你在不同程序之间用RE可能发现的区别?

当然Q你也可以在Visual C++~辑器中使用RE。选择Edit->ReplaceQ然后选择"Regular expression"选择框,Find What输入框对应上面介l的vi命o:%s/pat1/pat2/g中的pat1部分Q而Replace输入框对应pat2部分。但是,Z得到vi的执行范围和g选项Q你要用Replace All或者适当的手工Find Next and ReplaceQ译者按Q知道ؓ啥有人骂微Y弱智了吧Q虽然VC中可以选中一个范围的文本Q然后在其中执行替换Q但是M不够vi那么灉|和典雅)?/P>

sed

Sed?B>Stream EDitor的羃写,是Unix下常用的Z文g和管道的~辑工具Q可以在手册中得到关于sed的详l信息?

q里是一些有的sed脚本Q假定我们正在处理一个叫做price.txt的文件。注意这些编辑ƈ不会改变源文Ӟsed只是处理源文件的每一行ƈ把结果显C在标准输出中(当然很容易用重定向来定ӞQ?

sed脚本   描述


sed 's/^$/d' price.txt 删除所有空?/TD>
sed 's/^[ \t]*$/d' price.txt 删除所有只包含I格或者制表符的行
sed 's/"http://g' price.txt 删除所有引?/TD>

awk

awk是一U编E语aQ可以用来对文本数据q行复杂的分析和处理。可以在手册中得到关于awk的详l信息。这个古怪的名字是它作者们的姓的羃写(AhoQWeinberger和KernighanQ?

在AhoQWeinberger和Kernighan的书The AWK Programming Language中有很多很好的awk的例子,请不要让下面q些微不道的脚本例子限制你对awk强大能力的理解。我们同样假定我们针对price.txt文gq行处理Q跟sed一Pawk也只是把l果昄在终端上?nbsp;

awk脚本   描述


awk '$0 !~ /^$/' price.txt 删除所有空?/TD>
awk 'NF > 0' price.txt awk中一个更好的删除所有行的办?/TD>
awk '$2 ~ /^[JT]/ {print $3}' price.txt 打印所有第二个字段?J'或?T'打头的行中的W三个字D?/TD>
awk '$2 !~ /[Mm]isc/ {print $3 + $4}' price.txt 针对所有第二个字段不包?Misc'或?misc'的行Q打印第3和第4列的和(假定为数字)
awk '$3 !~ /^[0-9]+\.[0-9]*$/ {print $0}' price.txt 打印所有第三个字段不是数字的行Q这里数字是?TT>d.d或?TT>dq样的Ş式,其中d??的Q何数?/TD>
awk '$2 ~ /John|Fred/ {print $0}' price.txt 如果W二个字D包?John'或?Fred'则打印整?/TD>

grep

grep是一个用来在一个或者多个文件或者输入流中用REq行查找的程序。它的name~程语言可以用来针对文g和管道进行处理。可以在手册中得到关于grep的完整信息。这个同样古怪的名字来源于vi的一个命令,g/re/pQ意思是global regular expression print?

下面的例子中我们假定在文件phone.txt中包含以下的文本Q——其格式是姓加一个逗号Q然后是名,然后是一个制表符Q然后是电话LQ?/P>

    Francis, John           5-3871
    Wong, Fred              4-4123
    Jones, Thomas           1-4122
    Salazar, Richard        5-2522

grep命o   描述


grep '\t5-...1' phone.txt 把所有电话号码以5开头以1l束的行打印出来Q注意制表符是用\t表示?/TD>
grep '^S[^ ]* R' phone.txt 打印所有姓以S打头和名以R打头的行
grep '^[JW]' phone.txt 打印所有姓开头是J或者W的行
grep ', ....\t' phone.txt 打印所有姓?个字W的行,注意制表W是?B>\t表示?/TD>
grep -v '^[JW]' phone.txt 打印所有不以J或者W开头的?/TD>
grep '^[M-Z]' phone.txt 打印所有姓的开头是M到Z之间M字符的行
grep '^[M-Z].*[12]' phone.txt 打印所有姓的开头是M到Z之间M字符Qƈ且点号号码结是1或?的行

egrep

egrep是grep的一个扩展版本,它在它的正则表达式中支持更多的元字符。下面的例子中我们假定在文gphone.txt中包含以下的文本Q——其格式是姓加一个逗号Q然后是名,然后是一个制表符Q然后是电话LQ?

    Francis, John           5-3871
    Wong, Fred              4-4123
    Jones, Thomas           1-4122
    Salazar, Richard        5-2522

egrep command   Description


egrep '(John|Fred)' phone.txt 打印所有包含名?I>John或?I>Fred的行
egrep 'John|22$|^W' phone.txt 打印所有包?I>John 或者以22l束或者以W的行
egrep 'net(work)?s' report.txt 从report.txt中找到所有包?I>networks或?I>nets的行


正则表达式语法支持情?/H1>

命o或环?/B> . [ ] ^ $ \( \) \{ \} ? + | ( )
vi  X   X   X   X   X           
Visual C++  X   X   X   X   X           
awk  X   X   X   X       X   X   X   X 
sed  X   X   X   X   X   X         
Tcl  X   X   X   X   X     X   X   X   X 
ex  X   X   X   X   X   X         
grep  X   X   X   X   X   X         
egrep  X   X  X   X   X     X   X   X   X 
fgrep  X   X   X   X   X           
perl  X  X  X  X  X    X  X  X  X

 


vi替换命o?/H1>

Vi的替换命令:

    :ranges/pat1/pat2/g

其中

    : q是Vi的命令执行界面?
    range 是命令执行范围的指定Q可以用百分号Q?Q表C所有行Q用点Q?Q表C当前行Q用美元符P$Q表C最后一行。你q可以用行P例如10,20表示W?0?0行,.,$表示当前行到最后一行,.+2,$-5表示当前行后两行直到全文的倒数W五行,{等?

    s 表示其后是一个替换命令?/P>

    pat1 q是要查扄一个正则表辑ּQ这文章中有一大堆例子?/P>

    pat2 q是希望把匹配串变成的模式的正则表达式,q篇文章中有一大堆例子?

    g 可选标志,带这个标志表C替换将针对行中每个匚w的串q行Q否则则只替换行中第一个匹配串?/P>

|上有很多vi的在U手册,你可以访问他们以获得更加完整的信息?



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=539524



]]>
վ֩ģ壺 ŷպۺϾþþ| AV뾫Ʒɫҹ߹ۿ| ഺɫУ԰С˵| ˾Ʒѿ| ޾Ʒ޿| fc2˳ΪƵ| ձĻһ | Ƶ| þþƷav鶹ѿ| **ʵëƬѹۿ| 91Ʒѹۿ| ۺ͵Գҳɫ| Ƭ뿴| ޾ƷƬ| ձһѵӰ| **ɫëƬѹۿ| ĻþþƷAPP| ԺѵȫƵ| ޾Ʒ| պƷѵӰ| ˿Ƶ| ޱ龫Ʒһ| þ޾Ʒa| ĻmvѸƵ8 | ߹ۿAVպAV| Ʒþ㽶Ѳ| ɫwwwƵ߹ۿ| һ| ޾ƷƵ| ޾Ʒ˳| Ļһ| Ƶ̫ˬ| պɫƵһ| JIZZձ| ԻȫƵ߹ۿ | ĻþƷƵ| ŷ޹˾Ʒ| vavaպ߹ۿ| ëƬ߲ѹۿ| ޾Ʒa߹ۿ| һ|