Scala - заменить все - PullRequest
       32

Scala - заменить все

1 голос
/ 11 июня 2009

Прежде всего, я новичок в Scala.

Я пытаюсь сделать синтаксический анализатор шаблонов в Scala (аналог Smarty (PHP)). Необходимо выполнить поиск в документе, заменив что-либо внутри тегов "{{}}" на все, что предусмотрено в HashMap.

Я сейчас застрял здесь:

import scala.collection.mutable.HashMap
import scala.io.Source

class Template(filename: String, vars: HashMap[Symbol, Any]) {
  def parse() = { 
    var contents = Source.fromFile(filename, "ASCII").mkString
    var rule = """\{\{(.*)\}\}""".r

    //for(rule(v) <- rule findAllIn contents) {
    //  yield v
    //}

    //rule.replaceAllIn(contents, )
  }
}

var t = new Template("FILENAME", new HashMap[Symbol, Any])
println(t.parse)

Часть, которую я прокомментировал, - это то, о чем я думал.

Спасибо


Я прошел немного дальше ...

import scala.collection.mutable.HashMap
import scala.io.Source
import java.util.regex.Pattern
import java.util.regex.Matcher

class Template(filename: String, vars: HashMap[Symbol, Any]) {

  def findAndReplace(m: Matcher)(callback: String => String):String = {
    val sb = new StringBuffer
    while (m.find) { 
      m.appendReplacement(sb, callback(m.group(1))) 
    }
    m.appendTail(sb)
    sb.toString
  }

  def parse() = { 
    var contents = Source.fromFile(filename, "ASCII").mkString
    val m = Pattern.compile("""\{\{(.*)\}\}""").matcher(contents)

    findAndReplace(m){ x => x }

  }
}

var t = new Template("FILENAME.html", new HashMap[Symbol, Any])
println(t.parse)

В данный момент он просто добавляет то, что было внутри тега, обратно в документ. Мне интересно, есть ли более простой способ сделать регулярное выражение стиля поиска и замены в Scala?

Ответы [ 3 ]

2 голосов
/ 02 октября 2011

Есть вариант replaceAllIn в util.matching.Regex, который принимает replacer обратный вызов. Краткий пример:

import util.matching.Regex
def replaceVars(r: Regex)(getVar: String => String) = {
  def replacement(m: Regex.Match) = {
    import java.util.regex.Matcher
    require(m.groupCount == 1)
    Matcher.quoteReplacement( getVar(m group 1) )
  }
  (s: String) => r.replaceAllIn(s, replacement _)
}

Вот как мы будем его использовать:

val r = """\{\{([^{}]+)\}\}""".r
val m = Map("FILENAME" -> "aaa.txt",
            "ENCODING" -> "UTF-8")
val template = replaceVars(r)( m.withDefaultValue("UNKNOWN") )

println( template("""whatever input contains {{FILENAME}} and
unknown key {{NOVAL}} and {{FILENAME}} again,
and {{ENCODING}}""") )

Примечание Matcher.quoteReplacement экранирует $ символов в строке замены. В противном случае вы можете получить java.lang.IllegalArgumentException: Illegal group reference, replaceAll and dollar signs. См. сообщение в блоге о том, почему это может произойти.

2 голосов
/ 11 июня 2009

Я бы сделал это так (строка в качестве ключа вместо символа):

var s : String = input // line, whatever
val regexp = """pattern""".r

while(regexp findFirstIn s != None) {
  s = regexp replaceFirstIn (s, vars(regexp.findFirstIn(s).get))
}

Если вы предпочитаете не использовать var, переходите к рекурсии вместо использования while. И, конечно, струнный строитель будет более эффективным. В этом случае я мог бы сделать следующее:

val regexp = """^(.*?)(?:{{(pattern)}})?""".r
for(subs <- regexp findAllIn s)
  subs match {
    case regexp(prefix, var) => sb.append(prefix); if (var != null) sb.append("{{"+vars(var)+"}}")
    case _ => error("Shouldn't happen")
  }

Таким образом, вы продолжаете добавлять неизменяемую часть, после чего следует замена следующей части.

0 голосов
/ 17 сентября 2015

Вот также интересный способ сделать то же самое, используя функции compose:

  val Regexp = """\{\{([^{}]+)\}\}""".r

  val map = Map("VARIABLE1" -> "VALUE1", "VARIABLE2" -> "VALUE2", "VARIABLE3" -> "VALUE3")

  val incomingData = "I'm {{VARIABLE1}}. I'm {{VARIABLE2}}. And I'm {{VARIABLE3}}. And also {{VARIABLE1}}"


  def replace(incoming: String) = {
    def replace(what: String, `with`: String)(where: String) = where.replace(what, `with`)
    val composedReplace = Regexp.findAllMatchIn(incoming).map { m => replace(m.matched, map(m.group(1)))(_) }.reduceLeftOption((lf, rf) => lf compose rf).getOrElse(identity[String](_))
    composedReplace(incomingData)
  }

  println(replace(incomingData))

  //OUTPUT: I'm VALUE1. I'm VALUE2. And I'm VALUE3. And also VALUE1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...