scala Regex类用法示例源码详解
文章目录
此包与字符串的正则表达式(regex)匹配有关,其主要目标是从这些匹配中提取信息或将其替换为其他内容。
Regex
是用户实例化进行正则表达式匹配的类。
与
Regex
相关联的伴生对象包含支持成员:
Match
提供了有关匹配的更多信息。MatchIterator
用于迭代匹配的字符串。MatchData
只是上述类的基本特征。Groups
从Match
中提取组,而不重新计算匹配。
用法
这个类委托给Java平台的
java.util.regex
包。有关模式字符串的正则表达式语法的详细信息,请参阅
java.util.regex.Pattern
的文档。
Regex
的实例表示已编译的正则表达式模式。由于编译非常耗时,因此经常使用的
Regex
应该在循环之外构造一次,并且可能在伴生对象中。
创建
Regex
的规范方法是使用为字符串隐式提供的
r
方法:
val date =raw"(\d{4})-(\d{2})-(\d{2})".r
由于在多行字符串字面量中不会处理转义,因此使用三引号避免转义反斜杠字符,所以可以将
"\\d"
写成
"""\d"""
。使用某些插值器,例如
raw"\d".r
或自定义插值器
r"\d"
也可以实现相同的结果。
提取
要从匹配的
Regex
中提取捕获的组,可以在模式匹配中使用它作为提取器:
"2004-01-20"match{case date(year, month, day)=>s"$year was a good year for PLs."}
仅检查
Regex
是否匹配,忽略任何组,请使用序列通配符:
"2004-01-20"match{case date(_*)=>"It's a date!"}
这是因为
Regex
提取器会产生一个字符串序列。只从日期中提取年份也可以用序列通配符表示:
"2004-01-20"match{case date(year, _*)=>s"$year was a good year for PLs."}
在模式匹配中,
Regex
通常匹配整个输入。但是,未锚定的
Regex
会在输入的任何地方找到模式。
val embeddedDate = date.unanchored
"Date: 2004-01-20 17:25:18 GMT (10 years, 28 weeks, 5 days, 17 hours and 51 minutes ago)"match{case embeddedDate("2004","01","20")=>"A Scala is born."}
查找匹配
要查找或替换模式的匹配项,请使用各种查找和替换方法。对于每种方法,都有一个用于处理匹配字符串的版本,以及一个用于处理
Match
对象的版本。
例如,使用未锚定的
Regex
进行模式匹配,如上面的例子中所示,也可以使用
findFirstMatchIn
完成。
findFirst
方法返回一个
Option
,如果找到匹配,则为非空,否则为
None
:
val dates ="Important dates in history: 2004-01-20, 1958-09-05, 2010-10-06, 2011-07-15"val firstDate = date.findFirstIn(dates).getOrElse("No date found.")val firstYear =for(m <- date.findFirstMatchIn(dates))yield m.group(1)
要查找所有匹配项:
val allYears =for(m <- date.findAllMatchIn(dates))yield m.group(1)
要迭代匹配的字符串,请使用
findAllIn
,它返回一个特殊的迭代器,可以查询最后一个匹配的
MatchData
:
val mi = date.findAllIn(dates)while(mi.hasNext){val d = mi.next
if(mi.group(1).toInt <1960) println(s"$d: An oldie but goodie.")}
尽管
findAllIn
返回的
MatchIterator
像任何
Iterator
一样使用,交替调用
hasNext
和
next
,但是
hasNext
还具有将底层匹配器前进到下一个未使用的匹配的附加副作用。此效果在表示“当前匹配”的
MatchData
中可见。
val r ="(ab+c)".r
val s ="xxxabcyyyabbczzz"
r.findAllIn(s).start // 3val mi = r.findAllIn(s)
mi.hasNext // true
mi.start // 3
mi.next()// "abc"
mi.start // 3
mi.hasNext // true
mi.start // 9
mi.next()// "abbc"
该示例显示了如果需要,
MatchData
上的
start
等方法将前进到第一个匹配。它还显示了如果
next
已经返回当前匹配,
hasNext
将前进到下一个未使用的匹配。
可以使用
matchData
方法捕获当前
MatchData
。或者,
findAllMatchIn
返回一个
Iterator[Match]
,其中迭代器与其已经生成的
Match
对象之间没有交互。
请注意,
findAllIn
查找不重叠的匹配。 (有关更多示例,请参阅
findAllIn
。)
val num =raw"(\d+)".r
val all = num.findAllIn("123").toList // List("123"),而不是List("123", "23", "3")
替换文本
可以无条件地执行文本替换,也可以根据当前匹配来执行文本替换:
val redacted = date.replaceAllIn(dates,"XXXX-XX-XX")val yearsOnly = date.replaceAllIn(dates, m => m.group(1))val months =(0 to 11).map { i =>val c = Calendar.getInstance; c.set(2014, i,1);f"${c}%tb"}val reformatted = date.replaceAllIn(dates, _ match{case date(y,m,d)=>f"${months(m.toInt -1)}$d, $y"})
将
Match
与创建它的
Regex
进行模式匹配不会重新应用
Regex
。在
reformatted
的表达式中,每个
date
匹配只计算一次。但是可以将
Regex
应用于来自不同模式的
Match
:
val docSpree ="""2011(?:-\d{2}){2}""".r
val docView = date.replaceAllIn(dates, _ match{case docSpree()=>"Historic doc spree!"case _ =>"Something else happened"})
源码
/**
* 此包与字符串的正则表达式(regex)匹配有关,
* 其主要目标是从这些匹配中提取信息或将其替换为其他内容。
*
* [[scala.util.matching.Regex]] 是用户实例化进行正则表达式匹配的类。
*
* 与 [[scala.util.matching.Regex]] 相关联的伴生对象包含支持成员:
* * [[scala.util.matching.Regex.Match]] 提供了有关匹配的更多信息。
* * [[scala.util.matching.Regex.MatchIterator]] 用于迭代匹配的字符串。
* * [[scala.util.matching.Regex.MatchData]] 只是上述类的基本特征。
* * [[scala.util.matching.Regex.Groups]] 从 [[scala.util.matching.Regex.Match]] 中提取组,而不重新计算匹配。
*/packagescala.util.matchingimportscala.collection.AbstractIterator
importjava.util.regex.{ Pattern, Matcher }/** 一个正则表达式用于确定字符串是否匹配某个模式,
* 并在匹配成功后提取或转换匹配的部分。
*
* === 用法 ===
* 这个类委托给Java平台的[[java.util.regex]]包。
* 有关模式字符串的正则表达式语法的详细信息,请参阅[[java.util.regex.Pattern]]的文档。
*
* `Regex`的实例表示已编译的正则表达式模式。
* 由于编译非常耗时,因此经常使用的`Regex`应该在循环之外构造一次,并且可能在伴生对象中。
*
* 创建`Regex`的规范方法是使用为字符串隐式提供的`r`方法:
*
* {{{
* val date = raw"(\d{4})-(\d{2})-(\d{2})".r
* }}}
*
* 由于在多行字符串字面量中不会处理转义,因此使用三引号避免转义反斜杠字符,所以可以将`"\\d"`写成`"""\d"""`。
* 使用某些插值器,例如`raw"\d".r`或自定义插值器`r"\d"`也可以实现相同的结果。
*
* === 提取 ===
* 要从匹配的`Regex`中提取捕获的组,可以在模式匹配中使用它作为提取器:
*
* {{{
* "2004-01-20" match {
* case date(year, month, day) => s"\$year was a good year for PLs."
* }
* }}}
*
* 仅检查`Regex`是否匹配,忽略任何组,请使用序列通配符:
*
* {{{
* "2004-01-20" match {
* case date(_*) => "It's a date!"
* }
* }}}
*
* 这是因为`Regex`提取器会产生一个字符串序列。
* 只从日期中提取年份也可以用序列通配符表示:
*
* {{{
* "2004-01-20" match {
* case date(year, _*) => s"\$year was a good year for PLs."
* }
* }}}
*
* 在模式匹配中,`Regex`通常匹配整个输入。
* 但是,未锚定的`Regex`会在输入的任何地方找到模式。
*
* {{{
* val embeddedDate = date.unanchored
* "Date: 2004-01-20 17:25:18 GMT (10 years, 28 weeks, 5 days, 17 hours and 51 minutes ago)" match {
* case embeddedDate("2004", "01", "20") => "A Scala is born."
* }
* }}}
*
* === 查找匹配 ===
* 要查找或替换模式的匹配项,请使用各种查找和替换方法。
* 对于每种方法,都有一个用于处理匹配字符串的版本,以及一个用于处理`Match`对象的版本。
*
* 例如,使用未锚定的`Regex`进行模式匹配,如上面的例子中所示,也可以使用`findFirstMatchIn`完成。
* `findFirst`方法返回一个`Option`,如果找到匹配,则为非空,否则为`None`:
*
* {{{
* val dates = "Important dates in history: 2004-01-20, 1958-09-05, 2010-10-06, 2011-07-15"
* val firstDate = date.findFirstIn(dates).getOrElse("No date found.")
* val firstYear = for (m <- date.findFirstMatchIn(dates)) yield m.group(1)
* }}}
*
* 要查找所有匹配项:
*
* {{{
* val allYears = for (m <- date.findAllMatchIn(dates)) yield m.group(1)
* }}}
*
* 要迭代匹配的字符串,请使用`findAllIn`,它返回一个特殊的迭代器,
* 可以查询最后一个匹配的`MatchData`:
*
* {{{
* val mi = date.findAllIn(dates)
* while (mi.hasNext) {
* val d = mi.next
* if (mi.group(1).toInt < 1960) println(s"\$d: An oldie but goodie.")
* }
* }}}
*
* 尽管`findAllIn`返回的`MatchIterator`像任何`Iterator`一样使用,
* 交替调用`hasNext`和`next`,但是`hasNext`还具有将底层匹配器前进到下一个未使用的匹配的附加副作用。
* 此效果在表示“当前匹配”的`MatchData`中可见。
*
* {{{
* val r = "(ab+c)".r
* val s = "xxxabcyyyabbczzz"
* r.findAllIn(s).start // 3
* val mi = r.findAllIn(s)
* mi.hasNext // true
* mi.start // 3
* mi.next() // "abc"
* mi.start // 3
* mi.hasNext // true
* mi.start // 9
* mi.next() // "abbc"
* }}}
*
* 该示例显示了如果需要,`MatchData`上的`start`等方法将前进到第一个匹配。
* 它还显示了如果`next`已经返回当前匹配,`hasNext`将前进到下一个未使用的匹配。
*
* 可以使用`matchData`方法捕获当前`MatchData`。
* 或者,`findAllMatchIn`返回一个`Iterator[Match]`,其中迭代器与其已经生成的`Match`对象之间没有交互。
*
* 请注意,`findAllIn`查找不重叠的匹配。 (有关更多示例,请参阅[[findAllIn]]。)
*
* {{{
* val num = raw"(\d+)".r
* val all = num.findAllIn("123").toList // List("123"),而不是List("123", "23", "3")
* }}}
*
* === 替换文本 ===
* 可以无条件地执行文本替换,也可以根据当前匹配来执行文本替换:
*
* {{{
* val redacted = date.replaceAllIn(dates, "XXXX-XX-XX")
* val yearsOnly = date.replaceAllIn(dates, m => m.group(1))
* val months = (0 to 11).map { i => val c = Calendar.getInstance; c.set(2014, i, 1); f"\$c%tb" }
* val reformatted = date.replaceAllIn(dates, _ match { case date(y,m,d) => f"\${months(m.toInt - 1)} \$d, \$y" })
* }}}
*
* 将`Match`与创建它的`Regex`进行模式匹配不会重新应用`Regex`。
* 在`reformatted`的表达式中,每个`date`匹配只计算一次。 但是可以将`Regex`应用于来自不同模式的`Match`:
*
* {{{
* val docSpree = """2011(?:-\d{2}){2}""".r
* val docView = date.replaceAllIn(dates, _ match {
* case docSpree() => "Historic doc spree!"
* case _ => "Something else happened"
* })
* }}}
*
* @see [[java.util.regex.Pattern]]
*
* @param pattern 编译的模式
* @param groupNames 捕获组名称到索引的映射
*
* @define replacementString
* 在替换字符串中,以美元符号(`$`)后跟一个数字将被解释为对匹配的模式中的组的引用,
* 数字1到9对应于前九个组,数字0表示整个匹配。其他任何字符都是错误的。
* 反斜杠(`\`)字符将被解释为转义字符,并且可以用于转义美元符号。
* 使用`Regex.quoteReplacement`来转义这些字符。
*/@SerialVersionUID(-2094783597747625537L)class Regex private[matching](val pattern: Pattern, groupNames:String*)extends Serializable {
outer =>import Regex._
/** 编译正则表达式字符串成可匹配输入的模式。
*
* 如果提供了组名,则可以使用如下方式:
*
* {{{
* val namedDate = new Regex("""(\d\d\d\d)-(\d\d)-(\d\d)""", "year", "month", "day")
* val namedYears = for (m <- namedDate findAllMatchIn dates) yield m group "year"
* }}}
*
* 在提供了组名的情况下,优先使用构造函数中提供的组名来提取匹配的组。
* 并非所有平台都支持内联名称。
*
* 此构造函数不支持选项作为标志,而必须以模式字符串中的内联标志形式提供:`(?idmsux-idmsux)`。
*
* @param regex 要编译的正则表达式。
* @param groupNames 捕获组的名称。
*/defthis(regex:String, groupNames:String*)=this(Pattern.compile(regex), groupNames: _*)/** 尝试匹配[[java.lang.CharSequence]]。
*
* 如果匹配成功,则结果是匹配组的列表(如果组没有匹配任何输入,则为`null`元素)。
* 如果模式未指定组,则在成功匹配时结果将是一个空列表。
*
* 默认情况下,此方法尝试匹配整个输入;要查找下一个匹配的子序列,使用未锚定的`Regex`。
*
* 例如:
*
* {{{
* val p1 = "ab*c".r
* val p1Matches = "abbbc" match {
* case p1() => true // 没有组
* case _ => false
* }
* val p2 = "a(b*)c".r
* val p2Matches = "abbbc" match {
* case p2(_*) => true // 任意组
* case _ => false
* }
* val numberOfB = "abbbc" match {
* case p2(b) => Some(b.length) // 一个组
* case _ => None
* }
* val p3 = "b*".r.unanchored
* val p3Matches = "abbbc" match {
* case p3() => true // 查找b's
* case _ => false
* }
* val p4 = "a(b*)(c+)".r
* val p4Matches = "abbbcc" match {
* case p4(_*) => true // 多个组
* case _ => false
* }
* val allGroups = "abbbcc" match {
* case p4(all @ _*) => all mkString "/" // "bbb/cc"
* case _ => ""
* }
* val cGroup = "abbbcc" match {
* case p4(_, c) => c
* case _ => ""
* }
* }}}
*
* @param s 要匹配的字符串
* @return 匹配项
*/def unapplySeq(s: CharSequence): Option[List[String]]= s match{casenull=> None
case _ =>val m = pattern matcher s
if(runMatcher(m)) Regex.extractGroupsFromMatcher(m)else None
}/** 尝试匹配一个[[scala.Char]]的字符串表示形式。
*
* 如果匹配成功,则结果是第一个匹配组(如果定义了任何组),否则为空序列。
*
* 例如:
*
* {{{
* val cat = "cat"
* // 案例必须消耗组以进行匹配
* val r = """(\p{Lower})""".r
* cat(0) match { case r(x) => true }
* cat(0) match { case r(_) => true }
* cat(0) match { case r(_*) => true }
* cat(0) match { case r() => true } // 无匹配项
*
* // 没有要提取的组
* val r = """\p{Lower}""".r
* cat(0) match { case r(x) => true } // 无匹配项
* cat(0) match { case r(_) => true } // 无匹配项
* cat(0) match { case r(_*) => true } // 匹配
* cat(0) match { case r() => true } // 匹配
*
* // 即使有多个组,只返回一个
* val r = """((.))""".r
* cat(0) match { case r(_) => true } // 匹配
* cat(0) match { case r(_,_) => true } // 无匹配项
* }}}
*
* @param c 要匹配的Char
* @return 匹配项
*/def unapplySeq(c:Char): Option[List[Char]]={val m = pattern matcher c.toString
if(runMatcher(m)){if(m.groupCount >0) Some((m group 1).toList)else Some(Nil)}else None
}/** 尝试在一个[[scala.util.matching.Regex.Match]]上进行匹配。
*
* 先前失败的匹配结果为`None`。
*
* 如果成功匹配了当前模式,则使用该结果。
*
* 否则,将此`Regex`应用于先前匹配的输入,并使用该匹配的结果。
*/def unapplySeq(m: Match): Option[List[String]]=if(m ==null|| m.matched ==null) None
elseif(m.matcher.pattern ==this.pattern) Regex.extractGroupsFromMatch(m)else unapplySeq(m.matched)/** 尝试匹配目标。
* @param target 要匹配的字符串
* @return 匹配项
*/@deprecated("extracting a match result from anything but a CharSequence or Match is deprecated","2.11.0")def unapplySeq(target:Any): Option[List[String]]= target match{case s: CharSequence =>val m = pattern matcher s
if(runMatcher(m)) Regex.extractGroupsFromMatcher(m)else None
case m: Match => unapplySeq(m.matched)case _ => None
}// @see UnanchoredRegexprotecteddef runMatcher(m: Matcher)= m.matches()/** 将此`Regex`在给定字符序列中找到的所有非重叠匹配项作为[[scala.util.matching.Regex.MatchIterator]]返回,
* 它是一个特殊的[[scala.collection.Iterator]],返回匹配的字符串,
* 但也可以查询有关最后一次匹配的更多数据,例如捕获组和起始位置。
*
* `MatchIterator`也可以转换为返回类型为[[scala.util.matching.Regex.Match]]的迭代器,
* 这样通常由`findAllMatchIn`返回。
*
* 在潜在的匹配重叠的情况下,首先返回第一个可能的匹配,然后返回紧接着第一个匹配所消耗的输入的下一个匹配:
*
* {{{
* val hat = "hat[^a]+".r
* val hathaway = "hathatthattthatttt"
* val hats = hat.findAllIn(hathaway).toList // List(hath, hattth)
* val pos = hat.findAllMatchIn(hathaway).map(_.start).toList // List(0, 7)
* }}}
*
* 要返回重叠的匹配项,可以使用具有前瞻(`?=`)的正向预测断言制定一个不消耗重叠区域的正则表达式。
*
* {{{
* val madhatter = "(h)(?=(at[^a]+))".r
* val madhats = madhatter.findAllMatchIn(hathaway).map {
* case madhatter(x,y) => s"\$x\$y"
* }.toList // List(hath, hatth, hattth, hatttt)
* }}}
*
* 在耗尽迭代器后尝试检索匹配信息会导致[[java.lang.IllegalStateException]]。
* 有关详细信息,请参见[[scala.util.matching.Regex.MatchIterator]]。
*
* @param source 要匹配的文本。
* @return 匹配子字符串的[[scala.util.matching.Regex.MatchIterator]]。
* @example {{{for (words <- """\w+""".r findAllIn "A simple example.") yield words}}}
*/def findAllIn(source: CharSequence)=new Regex.MatchIterator(source,this, groupNames)/** 返回在给定字符序列中该正则表达式的所有非重叠匹配项作为
* [[scala.collection.Iterator]],其元素类型为[[scala.util.matching.Regex.Match]]。
*
* @param source 要匹配的文本。
* @return 所有匹配项的[[scala.collection.Iterator]],元素类型为[[scala.util.matching.Regex.Match]]。
* @example {{{for (words <- """\w+""".r findAllMatchIn "A simple example.") yield words.start}}}
*/def findAllMatchIn(source: CharSequence): Iterator[Match]={val matchIterator = findAllIn(source)new Iterator[Match]{def hasNext = matchIterator.hasNext
def next: Match ={
matchIterator.next()new Match(matchIterator.source, matchIterator.matcher, matchIterator.groupNames).force
}}}}/**
* 返回给定字符序列中此`Regex`的第一个匹配字符串的可选值,如果没有匹配则返回None。
*
* @param source 要匹配的文本。
* @return 包含文本中第一个匹配字符串的[[scala.Option]]。
* @example {{{"""\w+""".r findFirstIn "A simple example." foreach println // 输出 "A"}}}
*/def findFirstIn(source: CharSequence): Option[String]={val m = pattern.matcher(source)if(m.find) Some(m.group)else None
}/**
* 返回给定字符序列中此`Regex`的第一个匹配项,如果不存在则返回None。
*
* 如果匹配成功,则可以查询[[scala.util.matching.Regex.Match]]以获取更多数据。
*
* @param source 要匹配的文本。
* @return 包含文本中第一个匹配字符串的[[scala.Option]]。
* @example {{{("""[a-z]""".r findFirstMatchIn "A simple example.") map (_.start) // 返回 Some(2),表示文本中第一个匹配项的索引}}}
*/def findFirstMatchIn(source: CharSequence): Option[Match]={val m = pattern.matcher(source)if(m.find) Some(new Match(source, m, groupNames))else None
}/**
* 返回给定字符序列中此`Regex`在开头的可选匹配项,如果不匹配任何前缀则返回None。
*
* 与`findFirstIn`不同,此方法仅返回输入开头的匹配项。
*
* @param source 要匹配的文本。
* @return 匹配前缀的[[scala.Option]]。
* @example {{{"""\p{Lower}""".r findPrefixOf "A simple example." // 返回 None,因为文本不以小写字母开头}}}
*/def findPrefixOf(source: CharSequence): Option[String]={val m = pattern.matcher(source)if(m.lookingAt) Some(m.group)else None
}/**
* 返回给定字符序列中此`Regex`在开头的可选匹配项,如果不匹配任何前缀则返回None。
*
* 与`findFirstMatchIn`不同,此方法仅返回输入开头的匹配项。
*
* @param source 要匹配的文本。
* @return 匹配字符串的[[scala.Option]]。
* @example {{{"""\w+""".r findPrefixMatchOf "A simple example." map (_.after) // 返回 Some(" simple example.")}}}
*/def findPrefixMatchOf(source: CharSequence): Option[Match]={val m = pattern.matcher(source)if(m.lookingAt) Some(new Match(source, m, groupNames))else None
}/**
* 使用一个字符串替换所有匹配项。
*
* $replacementString
*
* @param target 要匹配的字符串
* @param replacement 将替换每个匹配项的字符串
* @return 结果字符串
* @example {{{"""\d+""".r replaceAllIn ("July 15", "<NUMBER>") // 返回 "July <NUMBER>"}}}
*/def replaceAllIn(target: CharSequence, replacement:String):String={val m = pattern.matcher(target)
m.replaceAll(replacement)}/**
* 使用替换函数替换所有匹配项。替换函数接受一个[[scala.util.matching.Regex.Match]],以便可以从匹配项中获取其他信息。
*
* @param target 要匹配的字符串。
* @param replacer 将匹配项映射到另一个字符串的函数。
* @return 替换后的目标字符串。
*/def replaceAllIn(target: CharSequence, replacer: Match =>String):String={val it =new Regex.MatchIterator(target,this, groupNames).replacementData
it foreach (md => it replace replacer(md))
it.replaced
}/**
* 使用返回[[scala.Option]]的替换函数替换部分匹配项。替换函数接受一个[[scala.util.matching.Regex.Match]],以便可以从匹配项中获取其他信息。
*
* @param target 要匹配的字符串。
* @param replacer 可选地将匹配项映射到另一个字符串的函数。
* @return 替换后的目标字符串。
*/def replaceSomeIn(target: CharSequence, replacer: Match => Option[String]):String={val it =new Regex.MatchIterator(target,this, groupNames).replacementData
for(matchdata <- it ; replacement <- replacer(matchdata))
it replace replacement
it.replaced
}/**
* 使用一个字符串替换第一个匹配项。
*
* $replacementString
*
* @param target 要匹配的字符串
* @param replacement 将替换匹配项的字符串
* @return 结果字符串
*/def replaceFirstIn(target: CharSequence, replacement:String):String={val m = pattern.matcher(target)
m.replaceFirst(replacement)}/**
* 在此正则表达式的匹配项周围拆分提供的字符序列。
*
* @param toSplit 要拆分的字符序列
* @return 根据此正则表达式的匹配项拆分的字符串数组
*/def split(toSplit: CharSequence): Array[String]=
pattern.split(toSplit)/**
* 创建一个具有相同模式但没有对提取器模式的整个字符串进行要求的新Regex。
*
* 通常,对`date`进行匹配的行为就好像模式被包含在锚定符中,`"^pattern\$"`。
*
* 未锚定的`Regex`的行为就好像这些锚定符已被移除。
*
* 注意,此方法实际上不会从模式中删除任何匹配器。
*
* 调用`anchored`返回原始的`Regex`。
*
* @return 新的未锚定的正则表达式
*/def unanchored: UnanchoredRegex =new Regex(pattern, groupNames: _*)with UnanchoredRegex {overridedef anchored = outer }/**
* 返回正则表达式的字符串表示形式。
*/def regex:String= pattern.pattern
/** 正则表达式的字符串表示形式。 */overridedef toString = regex
}/** 在模式匹配中使用时,返回第一个匹配项的[[Match]]的[[Regex]]。
*
* @see [[Regex#unanchored]]
*/trait UnanchoredRegex extends Regex {overrideprotecteddef runMatcher(m: Matcher)= m.find()overridedef unanchored =this}/** 此对象定义了描述正则表达式匹配和辅助对象的内部类。 */object Regex {/** 此类提供了访问匹配详细信息的方法。 */trait MatchData {/** 基本上包装了平台Matcher。 */protecteddef matcher: Matcher
/** 匹配源 */val source: CharSequence
/** 组的名称,如果没有定义则为空序列 */val groupNames: Seq[String]/** 模式中捕获组的数量。
* (对于给定的成功匹配,其中一些组可能没有匹配任何输入。)
*/def groupCount:Int/** 第一个匹配字符的索引,如果没有匹配则为-1 */def start:Int/** 第`i`个组中第一个匹配字符的索引,如果该组没有匹配任何内容则为-1 */def start(i:Int):Int/** 最后一个匹配字符之后的索引,如果没有匹配则为-1。 */def end:Int/** 第`i`个组中最后一个匹配字符之后的索引,如果该组没有匹配任何内容则为-1 */def end(i:Int):Int/** 匹配的字符串,如果没有匹配则为`null` */def matched:String=if(start >=0) source.subSequence(start, end).toString
elsenull/** 第`i`个组中的匹配字符串,如果没有匹配则为`null` */def group(i:Int):String=if(start(i)>=0) source.subSequence(start(i), end(i)).toString
elsenull/** 所有捕获组,即不包括group(0) */def subgroups: List[String]=(1 to groupCount).toList map group
/** 第一个匹配字符之前的字符序列,如果没有匹配则为`null` */def before: CharSequence =if(start >=0) source.subSequence(0, start)elsenull/** 第`i`个组中第一个匹配字符之前的字符序列,如果该组没有匹配任何内容则为`null` */def before(i:Int): CharSequence =if(start(i)>=0) source.subSequence(0, start(i))elsenull/** 最后一个匹配字符之后的字符序列,如果没有匹配则为`null` */def after: CharSequence =if(end >=0) source.subSequence(end, source.length)elsenull/** 第`i`个组中最后一个匹配字符之后的字符序列,如果该组没有匹配任何内容则为`null` */def after(i:Int): CharSequence =if(end(i)>=0) source.subSequence(end(i), source.length)elsenullprivatelazyval nameToIndex: Map[String,Int]= Map[String,Int]()++("":: groupNames.toList).zipWithIndex
/** 返回具有给定名称的组。
*
* 如果提供了显式组名称,则使用显式组名称;否则,查询内部实现以获取内联命名组。
* 并非所有平台都支持内联组名称。
*
* @param id 组名称
* @return 请求的组
* @throws IllegalArgumentException 如果请求的组名称未定义
*/def group(id:String):String=(if(groupNames.isEmpty)
matcher group id
else
nameToIndex.get(id)match{case Some(index)=> group(index)case None => matcher group id
})/** 匹配的字符串;等同于`matched.toString`。 */overridedef toString = matched
}/** 提供有关成功匹配的信息。 */class Match(val source: CharSequence,protected[matching]val matcher: Matcher,val groupNames: Seq[String])extends MatchData {/** 第一个匹配字符的索引。 */val start = matcher.start
/** 最后一个匹配字符之后的索引。 */val end = matcher.end
/** 子组的数量。 */def groupCount = matcher.groupCount
privatelazyval starts: Array[Int]=((0 to groupCount) map matcher.start).toArray
privatelazyval ends: Array[Int]=((0 to groupCount) map matcher.end).toArray
/** 第`i`个组中第一个匹配字符的索引。 */def start(i:Int)= starts(i)/** 第`i`个组中最后一个匹配字符之后的索引。 */def end(i:Int)= ends(i)/** 匹配本身,强制使用依赖于匹配器的lazy val,以便一旦匹配器被推进,匹配仍然有效。 */def force:this.type={ starts; ends;this}}/** 匹配的提取对象,产生匹配的字符串。
*
* 当您对匹配数据不感兴趣时,可以使用此提取对象来帮助编写替换函数。例如:
*
* {{{
* import scala.util.matching.Regex.Match
* """\w+""".r replaceAllIn ("A simple example.", _ match { case Match(s) => s.toUpperCase })
* }}}
*/object Match {def unapply(m: Match): Some[String]= Some(m.matched)}/** 提取对象,产生匹配中的组。使用此提取对象而不是原始`Regex`可确保不会重新计算匹配。
*
* {{{
* import scala.util.matching.Regex.Groups
*
* val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r
* val text = "The doc spree happened on 2011-07-15."
* val day = date replaceAllIn(text, _ match { case Groups(_, month, day) => s"\$month/\$day" })
* }}}
*/object Groups {def unapplySeq(m: Match): Option[Seq[String]]={if(m.groupCount >0) extractGroupsFromMatch(m)else None
}}privatedef extractGroupsFromMatch(m: Match): Option[List[String]]={var res = List.empty[String]var index = m.groupCount
while(index >0){
res ::= m.group(index)
index -=1}
Some(res)}privatedef extractGroupsFromMatcher(m: Matcher): Option[List[String]]={var res = List.empty[String]var index = m.groupCount
while(index >0){
res ::= m.group(index)
index -=1}
Some(res)}}/**
* 用于遍历正则表达式匹配项的类。
*
* 这是一个迭代器,返回匹配的字符串。
*
* 关于匹配数据的查询与底层匹配器的当前状态有关,调用`hasNext`或`next`会推进底层匹配器。
*
* 当匹配项用尽时,关于匹配数据的查询将抛出[[java.lang.IllegalStateException]]。
*
* @see [[java.util.regex.Matcher]]
*/class MatchIterator(val source: CharSequence,val regex: Regex,val groupNames: Seq[String])extends AbstractIterator[String]with Iterator[String]with MatchData {self=>protected[Regex]val matcher = regex.pattern.matcher(source)// 0 = 尚未匹配, 1 = 已匹配, 2 = 进入下一个匹配, 3 = 没有更多的匹配private[this]var nextSeen =0/** 如果`next`将找到一个匹配项,则返回true。
* 并且必要时推进底层匹配器;
* 对于当前匹配数据的查询与底层匹配器相关。
*/def hasNext:Boolean={
nextSeen match{case0=> nextSeen =if(matcher.find())1else3case1=>()case2=> nextSeen =0; hasNext
case3=>()}
nextSeen ==1// 否则,为3}/** 返回`source`的下一个匹配的子字符串。
* 并且必要时推进底层匹配器。
*/def next():String={
nextSeen match{case0=>if(!hasNext)thrownew NoSuchElementException ; next()case1=> nextSeen =2case2=> nextSeen =0; next()case3=>thrownew NoSuchElementException
}
matcher.group
}/** 报告是否为空。*/overridedef toString =super[AbstractIterator].toString
// 确保我们在匹配状态private[this]def ensure():Unit= nextSeen match{case0=>if(!hasNext)thrownew IllegalStateException
case1=>()case2=>()case3=>thrownew IllegalStateException
}/** 第一个匹配字符的索引。*/def start:Int={ ensure(); matcher.start }/** 第`i`个组中第一个匹配字符的索引。*/def start(i:Int):Int={ ensure(); matcher.start(i)}/** 最后一个匹配字符的索引。*/def end:Int={ ensure(); matcher.end }/** 第`i`个组中最后一个匹配字符之后的索引。*/def end(i:Int):Int={ ensure(); matcher.end(i)}/** 子组的数量。*/def groupCount ={ ensure(); matcher.groupCount }/** 转换为返回MatchData元素而不是String的迭代器。*/def matchData: Iterator[Match]=new AbstractIterator[Match]{def hasNext =self.hasNext
def next ={self.next();new Match(source, matcher, groupNames).force }}/** 转换为返回MatchData元素而不是String的迭代器,并具有替换支持。*/private[matching]def replacementData =new AbstractIterator[Match]with Replacement {def matcher =self.matcher
def hasNext =self.hasNext
def next ={self.next();new Match(source, matcher, groupNames).force }}}/**
* 一个特质,能够构建包含替换内容的字符串,假设它有一个匹配器。
* 可以与迭代器混合使用。
*/private[matching]trait Replacement {protecteddef matcher: Matcher
privateval sb =new java.lang.StringBuffer
def replaced ={val newsb =new java.lang.StringBuffer(sb)
matcher.appendTail(newsb)
newsb.toString
}def replace(rs:String)= matcher.appendReplacement(sb, rs)}/** 对正则表达式模式进行字面量引用。
*
* 输入中的所有正则表达式元字符在输出中以字面量形式匹配自身。
*
* @example {{{List("US\$", "CAN\$").map(Regex.quote).mkString("|").r}}}
*/def quote(text:String):String= Pattern quote text
/** 引用替换字符串以在替换方法中使用。
*
* 替换方法对替换字符串中的反斜杠(`\`)和美元符号(`\$`)具有特殊含义,因此它们不会被视为字面量。
* 此方法转义这些字符,以便可以将生成的字符串用作表示输入字符串的字面量替换。
*
* @param text 希望用作字面量替换的字符串。
* @return 可以将匹配项替换为`text`的字符串。
* @example {{{"CURRENCY".r.replaceAllIn(input, Regex quoteReplacement "US\$")}}}
*/def quoteReplacement(text:String):String= Matcher quoteReplacement text
}
版权归原作者 wang2leee 所有, 如有侵权,请联系我们删除。