2016-11-11 11 views
2

У нас есть файл, содержащий данные, которые мы хотим сопоставить с классом case. Я знаю достаточно, чтобы переборщить его, но ищет идиоматический способ в scala.Каков наилучший способ сопоставления строк в файле с классом case в Scala?

Учитывая Файл:

#record 
name:John Doe 
age: 34 

#record 
name: Smith Holy 
age: 33 

# some comment 

#record 
# another comment 
name: Martin Fowler 
age: 99 

(значения поля на две строки являются недопустимыми, например, имя: Джон \ п Смит должен ошибка)

И дело класс

case class Record(name:String, age:Int) 

I Want для возврата типа Seq, такого как поток:

val records: Stream records 

Пара идей я работаю, но до сих пор не реализованы является:

  1. Удалить все новые линии и обработать весь файл как одну длинную строку. Тогда grep соответствует строке "((?! Name).) + ((?! age).) + Age: ([\ s \ d] +)" и создайте новый объект класса case для каждого совпадения, но пока мое регулярное выражение foo низкое и не может сравниться с комментариями.

  2. Рекурсивная идея: Итерации по каждой строке, чтобы найти первую строку, которая соответствует записи, а затем рекурсивно вызывать функцию для соответствия имени, а затем возрасту. Хвост рекурсивно вернуть Some(new Record(cumulativeMap.get(name), cumulativeMap.get(age)) или None при ударе следующей record после name (т.е. age никогда не встречается)

  3. ?? Лучшая идея?

Спасибо за чтение! Файл сложнее, чем выше, но все правила равны. Для любопытных: я пытаюсь разобрать пользовательский формат файла плейлиста M3U.

ответ

1

Вы можете использовать Parser Combinators.

Если у вас есть спецификация формата файла в BNF или вы можете написать один, то Scala может создать для вас парсер из этих правил. Это может быть более надежным, чем парсеры на основе регулярных выражений. Это, безусловно, больше «Скала».

+0

Я думаю, что лучший вариант Scala здесь. В моей реальной работе у меня слишком много полей, чтобы сидеть здесь и соответствовать регулярному выражению для каждого из них. Также есть поля заголовка. Я думаю, что это будет путь. Я проверю это. – dlite922

1

У меня нет большого опыта в Scala, но могли бы работать эти регулярные выражения:

Вы можете использовать (?<=name:).*, чтобы соответствовать значению имени и (?<=age:).* соответствовать значению возраста. Если вы используете это, удалите пробелы в найденных совпадениях, иначе name: bob будет соответствовать bob с пробелом раньше, вам может и не понадобиться.

Если name: или любой другой тег в комментарии, или комментарий после значения, что-то будет соответствовать. Пожалуйста, оставьте комментарий, если вы хотите этого избежать.

+0

Нет, это не работает. Я могу использовать 'sed' для удаления любой строки, начинающейся с хэша, которая не является' # record'. Он должен удалить все комментарии! Благодаря! – dlite922

1

Вы можете попробовать это:

Path file = Paths.get("file.txt"); 
val lines = Files.readAllLines(file, Charset.defaultCharset()); 

val records = lines.filter(s => s.startsWith("age:") || s.startsWith("name:")) 
        .grouped(2).toList.map { 
    case List(a, b) => Record(a.replaceAll("name:", "").trim, 
          b.replaceAll("age:", "").trim.toInt) 
} 
2

Я использовал бы kantan.regex для довольно тривиального решения на основе регулярных выражений.

Без фантазии бесформенный вывода, вы можете написать следующее:

import kantan.regex._ 
import kantan.regex.implicits._ 

case class Record(name:String, age:Int) 
implicit val decoder = MatchDecoder.ordered(Record.apply _) 
input.evalRegex[Record](rx"(?:name:\s*([^\n]+))\n(?:age:\s*([0-9]+))").toList 

Это дает:

List(Success(Record(John Doe,34)), Success(Record(Smith Holy,33)), Success(Record(Martin Fowler,99))) 

Обратите внимание, что это решение требует, чтобы вручную писать decoder, но это часто может быть автоматически получен. Если вы не возражаете против бесформенной зависимости, вы можете просто написать:

import kantan.regex._ 
import kantan.regex.implicits._ 
import kantan.regex.generic._ 

case class Record(name:String, age:Int) 
input.evalRegex[Record](rx"(?:name:\s*([^\n]+))\n(?:age:\s*([0-9]+))").toList 

И получите тот же результат.

Отказ от ответственности: Я автор библиотеки.