2012-10-12 152 views
6

Me gustaría saber cómo puedo dividir una cadena usando más de un delimitador con Scala.Scala: cómo dividir usando más de un delimitador

Por ejemplo, si tengo una lista de delimitadores:

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

Y una cadena de cosechar:

Car foerjfpoekrfopekf Red ezokdpzkdpoedkzopke dekpzodk Foo azdkpodkzed 

me gustaría ser capaz de salida de algo como:

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

Respuesta

1

un poco prolijo, pero funciona:
versión obsoleta: (se tiene un error, lo dejó aquí porque ya aceptó la respuesta)

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" 

de salida:

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

vuelve Array[String] lugar de la lista. pero se puede hacer igual de bien: f(s, l, g).toList

EDIT: acabo de notar que este código es bueno si los delimitadores sólo aparecen una vez en la cadena. si había definido s de la siguiente manera:

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

yo todavía obtener el mismo resultado, en lugar de otro par ("Car"," more...")

editar # 2: Bugless VERSIÓN aquí está el fragmento fijo:

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

salida:

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

y ahora funciona :)

+0

muchas gracias! esto es exactamente lo que estaba tratando de hacer – Roch

+1

no hay problema ...de hecho lo disfruté :) –

+0

que es terrible de leer y, como ya se ha demostrado, propenso a errores –

7

Puede usar la lista para crear una expresión regular y usar su método de división:

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

Sin embargo, eso no le dice qué delimitador se utilizó donde. Si lo necesitas, te sugiero que pruebes la biblioteca de analizadores de Scala.

EDIT:

O puede utilizar expresiones regulares para extraer un par en un momento como este:

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

O '.mkString (" \\ Q "," \\ E | \\ Q "," \\ E ")' para (con suerte) reducir el número posible de expresiones regulares exploits. Aunque no estoy seguro de que esto funcione. – Debilski

+1

seguro, si la lista proviene de alguna fuente externa, pero entonces uno debe usar '.map (java.util.regex.Pattern.quote)' –

+0

Tiene más sentido. No sabía eso. – Debilski

Cuestiones relacionadas