2009-06-10 6 views
1

Zunächst einmal, ich bin neu in Scala.Scala - replaceAllIn

Ich versuche, einen Template-Parser in Scala zu machen (ähnlich Smarty (PHP)). Es muss das Dokument durchsuchen und alles in "{{}}" -Tags durch etwas ersetzen, das in der HashMap bereitgestellt wird.

Ich bin derzeit hier fest:

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) 

Der Teil der, die ich habe, sind Dinge, kommentierte, dass ich gedacht habe über das tun.

Dank


Ich habe ein wenig weiter kommen ...

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) 

Im Moment ist es zur Zeit nur, was innerhalb des Tags war fügt hinzu, zurück in das Dokument. Ich frage mich, ob es einen einfacheren Weg gibt, in Scala einen Regexp zu finden und zu ersetzen.

+0

Es tut mir leid - was ist Ihre Frage? –

+0

In dem Dokument wird es Variablen geben, die von "{{" und "}}" z. "{{ Name }}". Ich muss den besten Weg finden, jede Instanz von "{{WHATEVER}}" mit einem Wert innerhalb der Hashmap zu tauschen. –

Antwort

2

Ich würde es tun, wie dieser (String als Schlüssel statt Symbol):

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

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

Wenn Sie nicht var lieber verwenden, gehen rekursiv statt während der Verwendung. Und natürlich wäre ein Stringbuilder effizienter. In diesem Fall könnte ich folgendes:

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") 
    } 

diese Weise können Sie das Anhängen halten die sich nicht ändernden Teil durch den nächsten Teil gefolgt ersetzt werden.

2

Es gibt eine Variante von replaceAllIn in util.matching.Regex, die eine replacer Rückruf akzeptiert. Ein kurzes Beispiel:

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 _) 
} 

Dies ist, wie wir sie verwenden würde:

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}}""")) 

HinweisMatcher.quoteReplacement entkommt $ Zeichen im Ersetzungsstring. Andernfalls erhalten Sie möglicherweise java.lang.IllegalArgumentException: Illegal group reference, replaceAll and dollar signs. Siehe the blog post warum dies passieren kann. Hier

0

ist auch interessant, Art und Weise, wie die gleichen Funktionen verwenden zu tun komponieren:

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