2015-07-14 2 views
0

У меня есть текстовый файл со следующим содержанием.Как преобразовать форматированную строку в кортеж в Scala?

//((number,(number,date)),number) 
((210,(18,2015/06/28)),57.0) 
((92,(60,2015/06/16)),102.89777479000209) 
((46,(18,2015/06/17)),52.8940162267246) 
((204,(27,2015/06/06)),75.2807019793683) 

Я хочу преобразовать его в кортеж и выполнить быстрый способ его выполнения. Поскольку список таких строк у меня существенно огромный.

EDIT: Я также хотел бы сохранить информацию о типе и структуре.

Любая помощь будет оценена по достоинству.

ответ

2

Я нахожу scala-парсер-комбинаторы - хороший способ сделать это; это намного более самодокументирован чем расколы или регулярных выражений:

import scala.util.parsing.combinator.JavaTokenParsers 
import org.joda.time.LocalDate 

object MyParser extends JavaTokenParsers { 
    override val skipWhitespace = false 
    def date = (wholeNumber ~ "/" ~ wholeNumber ~ "/" ~ wholeNumber) ^^ { 
    case day ~ _ ~ month ~ _ ~ year => 
     new LocalDate(year.toInt, month.toInt, day.toInt) 
    } 
    def myNumber = decimalNumber ^^ { _.toDouble } 
    def tupleElement: Parser[Any] = date | myNumber | tuple 
    def tuple: Parser[List[Any]] = "(" ~> repsep(tupleElement, ",") <~ ")" 
    def data = repsep(tuple, "\\n") 
} 

Надеюсь, способ продлить это очевидно. Использование что-то вроде:

scala> MyParser.parseAll(MyParser.data, """((210,(18,2015/06/28)),57.0) 
| ((92,(60,2015/06/16)),102.89777479000209) 
| ((46,(18,2015/06/17)),52.8940162267246) 
| ((204,(27,2015/06/06)),75.2807019793683)""") 
res1: MyParser.ParseResult[List[List[Any]]] = [4.41] parsed: List(List(List(210, List(18, LocalDate(28,6,2015))), 57.0), List(List(92, List(60, LocalDate(16,6,2015))), 102.89777479000209), List(List(46, List(18, LocalDate(17,6,2015))), 52.8940162267246), List(List(204, List(27, LocalDate(6,6,2015))), 75.2807019793683)) 

типы не могут быть полностью известны во время компиляции (сокр делать синтаксический во время компиляции с макро или некоторые такие) - выше является List[List[Any]] где элементы либо LocalDate, Double или другой List. Вы можете справиться с ним, используя сопоставление шаблонов во время выполнения. Приятнее подход может быть использование герметичного черта:

sealed trait TupleElement 
case class NestedTuple(val inner: List[TupleElement]) extends TupleElement 
case class NumberElement(val value: Double) extends TupleElement 
case class DateElement(val value: LocalDate) extends TupleElement 

def myNumber = decimalNumber ^^ { d => NumberElement(d.toDouble) } 
def tupleElement: Parser[TupleElement] = ... //etc. 

Затем, когда у вас есть в коде на TupleElement и вы шаблон матча, компилятор будет выдавать предупреждение, если вы не охватывают все случаи.

3

супер простой способ:

val splitRegex = "[(),]+".r 
def f(s: String) = { 
    val split = splitRegex.split(s) 
(split(1).toInt, split(2).toInt, split(3), split(4).toDouble) 
} 

f("((210,(18,2015/06/28)),57.0)") 
// res0: (Int, Int, String, Double) = (210.0,18.0,2015/06/28,57.0) 

уборщик путь:

val TupleRegex = """\(\((\d+),\((\d+),(\d+/\d+/\d+)\)\),([\d.]+)\)""".r 
def f(s: String) = s match { 
    case TupleRegex(n1, n2, d, n3) => (n1.toInt, n2.toInt, d, n3.toDouble) 
} 

f("((210,(18,2015/06/28)),57.0)") 
// res1: (Int, Int, String, Double) = (210.0,18.0,2015/06/28,57.0) 
+0

Спасибо, хорошо выглядит. И еще более ориентированный на Скала ответ :) –

1

Предполагая, что струны все хорошо сформированы, регулярные выражения, расщепление и синтаксического анализа будет достаточно быстро. Вы не упомянули, если вы хотите сохранить структуру исходных данных (и типы усиления) или просто мешок кортежей, но либо достаточно легко:

val strings = Array("((210,(18,2015/06/28)),57.0)", 
    "((92,(60,2015/06/16)),102.89777479000209)", 
    "((46,(18,2015/06/17)),52.8940162267246)", 
    "((204,(27,2015/06/06)),75.2807019793683)") 

val dateFormat = new java.text.SimpleDateFormat("yyyy/MM/dd") 

def toUnstructuredTuple(s:String):(Int, Int, java.util.Date, Double) = { 
    val noParens = s.replaceAll("[\\(\\)]", "") 
    val split = noParens.split(",") 

    (split(0).toInt, split(1).toInt, dateFormat.parse(split(2)), split(3).toDouble) 
} 

def toStructedTuple(s:String):((Int,(Int, java.util.Date)), Double) = { 
    val noParens = s.replaceAll("[\\(\\)]", "") 
    val split = noParens.split(",") 

    ((split(0).toInt, (split(1).toInt, dateFormat.parse(split(2)))), split(3).toDouble) 
} 


strings.foreach { s => 
    println("%s -> %s".format(s, toUnstructuredTuple(s))) 
} 


strings.foreach { s => 
    println("%s -> %s". format(s, toStructedTuple(s))) 
} 

Это приводит к:

benderino 21:54 $ bin/scala tuples.scala 
((210,(18,2015/06/28)),57.0) -> (210,18,Sun Jun 28 00:00:00 PDT 2015,57.0) 
((92,(60,2015/06/16)),102.89777479000209) -> (92,60,Tue Jun 16 00:00:00 PDT 2015,102.89777479000209) 
((46,(18,2015/06/17)),52.8940162267246) -> (46,18,Wed Jun 17 00:00:00 PDT 2015,52.8940162267246) 
((204,(27,2015/06/06)),75.2807019793683) -> (204,27,Sat Jun 06 00:00:00 PDT 2015,75.2807019793683) 
((210,(18,2015/06/28)),57.0) -> ((210,(18,Sun Jun 28 00:00:00 PDT 2015)),57.0) 
((92,(60,2015/06/16)),102.89777479000209) -> ((92,(60,Tue Jun 16 00:00:00 PDT 2015)),102.89777479000209) 
((46,(18,2015/06/17)),52.8940162267246) -> ((46,(18,Wed Jun 17 00:00:00 PDT 2015)),52.8940162267246) 
((204,(27,2015/06/06)),75.2807019793683) -> ((204,(27,Sat Jun 06 00:00:00 PDT 2015)),75.2807019793683) 
Смежные вопросы