2016-09-20 4 views
0

など。タスクはのコレクションとして3つの連続ブロックが含まれています注文/番コレクション(マップ、配列または何でも)には、このファイルを解析することであるファイルをScalaの連続した非空行ブロックに分割する方法は?

aaa 
    bbb 
ccc 

dd dd 
eee 
fff 

gg 
hhhhh 

:私たちは、以下の内容のファイルを持っています文字列。

アルゴリズム的なJava風味の方法はかなりわかりやすいようですが、誰かが機能的なScala-idiomaticソリューションを提案できれば素晴らしいかもしれません。任意の区切り文字(|)と別々のブロックにグループ化することで

答えて

0

分割は:いくつかの技巧で

scala> def chunks(s: Stream[String]): Stream[Seq[String]] = { 
    | val (h, t) = s.span(_.nonEmpty) 
    | h.toSeq #:: chunks(t.tail) } 
chunks: (s: Stream[String])Stream[Seq[String]] 

val blocks: List[List[String]] = Source 
    .fromFile("<path-to-file>").getLines() 
    .mkString("|") 
    .split("\\|{2,}").toList 
    .map(_.split("\\|").toList) 

これは、あなた

List(List(aaa, bbb, ccc), List(dd dd, eee, fff), List(gg, hhhhh)) 
+0

としてかなりきちんとしています合理的に大きなファイルで多くの望ましくない問題が発生します。そして、メモリ内のすべてのファイルを読み込みたい場合は、 'mkString'を実行する必要はありません。各要素で' isEmpty'チェックを使用するだけでリストを分割できます。あなたは記憶に結果を残していますが、とにかくそれは問題にならないでしょう。しかし、もしそれらの連続したブロックをストリーミングしたいなら、そのすべてをメモリに読み込まないようにすべきです。 –

3

使用Stream.spanを与えます:

scala> def chunks(s: Stream[String]): Stream[Stream[String]] = { 
    | val (h, t) = s.span(_.nonEmpty) 
    | if (h.isEmpty) Stream.empty else h #:: chunks(t drop 1) } 
chunks: (s: Stream[String])Stream[Stream[String]] 

scala> val cs = chunks(lines.lines.toStream).iterator 
cs: Iterator[Stream[String]] = non-empty iterator 

scala> cs.next.toList 
res0: List[String] = List(aaa, " bbb", ccc) 

scala> cs.next.toList 
res1: List[String] = List(dd dd, eee, fff) 

scala> cs.next.toList 
res2: List[String] = List(gg, hhhhh) 

scala> cs.hasNext 
res3: Boolean = false 
0

使用して、それを変換するために、スプリットを使用することができます。

def contigSplit(s : String) : Array[Array[String]] = s.split("\n\n").map(_.split("\n")) 

連続ブロックが2つの改行で終了しているため、この作品。

REPLの使用法:

scala> val s = """ 
    | aaa 
    | bbb 
    | ccc 
    | 
    | dd dd 
    | eee 
    | fff 
    | 
    | gg 
    | hhhhh 
    | """ 

scala> s.split("\n\n").map(_.split("\n")) 
res7: Array[Array[String]] = Array(Array("", aaa, " bbb", ccc), Array(dd dd, eee, fff), Array(gg, hhhhh)) 

オルタナティブ:空白行は、他の空白文字が含まれている可能性がある場合

、正規表現の分割を使用することができます。

def contigSplitRegEx(s : String) : Array[Array[String]] = "\n\\s*\n".r.split(s).map(_.split("\n")) 
0
def getListOfContguousLists(iterator: Iterator[String]): List[List[String]] = { 
    val (listOfContiguousList, lastList) = iterator 
    .foldLeft((List.empty[List[String]], List.empty[String]))({ 
     case ((listOfLists, list), line) => (line.isEmpty, list.isEmpty) match { 
     case (true, true) => (listOfLists, list) 
     case (true, false) => (listOfLists :+ list, List.empty[String]) 
     case (false, _) => (listOfLists, list :+ line) 
     } 
    }) 
    lastList.isEmpty match { 
    case true => listOfContiguousList 
    case false => listOfContiguousList :+ lastList 
    } 
} 

val list = getListOfContiguousLists(scala.io.Source.fromFile("").getLines) 
0

をマイ番号のバケットのマップntainとラインに

Source.fromFile("some_file").getLines().toList.filterNot(_.isEmpty) 
     .zipWithIndex 
     .foldLeft(Map(0 -> List.empty[String], 1 -> List.empty[String], 2 -> List.empty[String])) { (result, current) => 
     result.updated(current._2 % 3, result(current._2 % 3) ++ List(current._1)) 
     } 
0

を置くためにバケツを決定するために行番号%3を取ることは、純粋に機能していないのですが、 `mkString`をすることをイテレータ

def groupBlanksIterator(xs:Iterator[String]) = 
new Iterator[List[String]] 
    { def hasNext = xs.hasNext; def next = xs.takeWhile(_.nonEmpty).toList} 

groupBlanksIterator(scala.io.Source.fromFile("whatever").getLines) 
関連する問題