2012-10-12 9 views
6

Ich würde gerne wissen, wie ich eine Zeichenfolge mit mehr als einem Trennzeichen mit Scala teilen kann.Scala: wie mit mehr als einem Trennzeichen zu teilen

Zum Beispiel, wenn ich eine Liste mit Trennzeichen haben:

List("Car", "Red", "Boo", "Foo") 

Und eine Zeichenfolge ernten:

List( ("Car", " foerjfpoekrfopekf "), 
    ("Red", " ezokdpzkdpoedkzopke dekpzodk "), 
    ("Foo", " azdkpodkzed")  
) 
:

Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed 

Ich mag die Ausgabe etwas in der Lage sein möchte

Antwort

1

ein bisschen ausführlicher, aber es funktioniert:
DEPRECATED VERSION: (it hat einen Fehler, hier ist es, weil Sie die Antwort bereits akzeptiert haben)

def f(s: String, l: List[String], g: (String, List[String]) => Int) = { 
    for { 
     t <- l 
     if (s.contains(t)) 
     w = s.drop(s.indexOf(t) + t.length) 
    } yield (t, w.dropRight(w.length - g(w, l))) 
} 

def h(s: String, x: String) = if (s.contains(x)) s.indexOf(x) else s.length 

def g(s: String, l: List[String]): Int = l match { 
    case Nil => s.length 
    case x :: xs => math.min(h(s, x), g(s, xs)) 
} 

val l = List("Car", "Red", "Boo", "Foo") 

val s = "Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed" 

Ausgabe:

f(s, l, g).foreach(println) 
> (Car, foerjfpoekrfopekf) 
> (Red, ezokdpzkdpoedkzopke dekpzodk) 
> (Foo, azdkpodkzed) 

es Array[String] statt Liste zurückgibt. Sie können aber genauso gut tun: f(s, l, g).toList

EDIT: habe gerade bemerkt, dieser Code ist gut, wenn die Trennzeichen nur einmal in der Zeichenfolge erscheinen. wenn hatte s wie folgt definiert:

val s = "Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed Car more..." 

ich immer noch das gleiche Ergebnis würde, anstatt ein anderes Paar ("Car"," more...")

EDIT # 2: bugless VERSION hier ist der feste Schnipsel:

def h(s: String, x: String) = if (s.contains(x)) s.indexOf(x) else s.length 

def multiSplit(str: String, delimiters: List[String]): List[(String, String)] = { 
    val del = nextDelimiter(str, delimiters) 
    del._1 match { 
     case None => Nil 
     case Some(x) => { 
      val tmp = str.drop(x.length) 
      val current = tmp.dropRight(tmp.length - nextDelIndex(tmp,delimiters)) 
      (x, current) :: multiSplit(str.drop(x.length + current.length), delimiters) 
     } 
    } 
} 

def nextDelIndex(s: String, l: List[String]): Int = l match { 
    case Nil => s.length 
    case x :: xs => math.min(h(s, x), nextDelIndex(s, xs)) 
} 

def nextDelimiter(str: String, delimiters: List[String]): (Option[String], Int) = delimiters match { 
    case Nil => (None, -1) 
    case x :: xs => { 
     val next = nextDelimiter(str, xs) 
     if (str.contains(x)) { 
      val i = str.indexOf(x) 
      next._1 match { 
       case None => (Some(x), i) 
       case _ => if (next._2 < i) next else (Some(x), i) 
      } 
     } else next 
    } 
} 

Ausgabe:

multiSplit(s, l).foreach(println) 
> (Car, foerjfpoekrfopekf) 
> (Red, ezokdpzkdpoedkzopke dekpzodk) 
> (Foo, azdkpodkzed) 
> (Car, more...) 

und jetzt funktioniert es :)

+0

vielen Dank! Genau das habe ich versucht – Roch

+1

kein Problem ...Ich genoss es wirklich :) –

+0

das ist schrecklich zu lesen und - wie bereits gezeigt - fehleranfällig –

7

Sie können die Liste verwenden, um einen regulären Ausdruck zu erstellen und seine Split-Methode zu verwenden:

val regex = List("Car", "Red", "Boo", "Foo").mkString("|").r 
regex.split("Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed") 

Das sagt Ihnen jedoch nicht, welches Trennzeichen wo verwendet wurde. Wenn Sie das brauchen, schlage ich vor, dass Sie die Parser-Bibliothek von Scala ausprobieren.

EDIT:

Oder Sie reguläre Ausdrücke verwenden können, wie dieses Paar zu einer Zeit, zu extrahieren:

def split(s:String, l:List[String]):List[(String,String)] = { 
    val delimRegex = l.mkString("|") 
    val r = "("+delimRegex+")(.*?)(("+delimRegex+").*)?" 
    val R = r.r 
    s match { 
    case R(delim, text, rest, _) => (delim, text) :: split(rest, l) 
    case _ => Nil 
    } 
} 
+0

Oder '.mkString ("\\ Q", "\\ E | \\ Q", "\\ E")' auf (hoffentlich) reduzieren die Anzahl der möglichen regex Exploits. Nicht wirklich sicher, dass das funktioniert. – Debilski

+1

sicher, wenn die Liste von einer externen Quelle stammt, aber dann sollte man '.map (java.util.regex.Pattern.quote)' –

+0

macht Sinn. Ich kannte das nicht. – Debilski

Verwandte Themen