2014-01-30 4 views
0

Я пытаюсь улучшить процедуру синтаксического анализа CSV и считаю, что экстракторы могут быть полезны здесь, но не могут понять их. Предположим, что существует файл с пользовательскими идентификаторами и электронной почты:Использование экстракторов для разбора текстовых файлов

1,[email protected] 
2,[email protected] 
3,[email protected] 

Если User класс определяется как case class User(id: Int, email: String) все довольно просто с чем-то вроде

lines map { line => 
    line split "," match { 
    case Array(id, email) => User(id.toInt, email) 
    } 
} 

То, что я не понимаю, как иметь дело с случай, когда User класс может иметь сложные свойства, например

case class Email(username: String, host: string) 
case class User(id: Int, email: Email) 

ответ

0

Здесь вы найдете пример использования пользовательского экстрактора.

// 1,Alice,21212,Baltimore,MD" -> User(1, Alice, Address(21212, Baltimore, MD)) 

Определение пользовательского экстрактор, который создает объекты выхода из данной строки:

object UserExtractor { 
    def unapply(s: String) : Option[User] = try { 
     Some(User(s)) 
    } 
    catch { 
     // bettor handling of bad cases 
     case e: Throwable => None 
    } 
} 

классы Case для хранения данных с обычаем применять на объекте Comapnion на пользователя:

case class Address(code: String, cit: String, county: String) 

case class User(id: Int, name: String, address: Address) 
object User { 
    def apply(s: String) : User = s.split(",") match { 
     case Array(id, name, code, city, county) => User(id.toInt, name, Address(code, city, county) ) 
    } 
} 

Неприменение на допустимой строке (в примере valid указано правильное количество полей).

"1,Alice,21212,Baltimore,MD" match { case UserExtractor(u) => u } 
res0: User = User(1,Alice,Address(21212,Baltimore,MD)) 

Дополнительные тесты могут быть добавлены с использованием более настраиваемых методов.

2

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

val lines = Vector(
    "1,[email protected]", 
    "2,[email protected]", 
    "3,[email protected]") 

case class Email(username: String, host: String) 
case class User(id: Int, email: Email) 

val EmailRe = """(.*)@(.*\.com)""".r // substitute a real email address regex here 

lines.map { line => 
    line.split(",") match { 
    case Array(id, EmailRe(uname, host)) => User(id.toInt, Email(uname, host)) 
    } 
} 
+0

Это не выглядит очень гибким для меня. Со всеми новыми доменами верхнего уровня, которые попадают в ближайшее время, я не думаю, что вы захотите сопоставить «.com» или любой другой TLD, если на то пошло. –

+2

@James: Очевидно, вы хотели бы заменить свое собственное регулярное выражение для адресов электронной почты.Они сложны, и вы можете легко найти один с быстрым поиском в Интернете. – dhg

0

Я хотел бы использовать один RegexExtractor:

val lines = List(
    "1,[email protected]", 
    "2,[email protected]", 
    "3,[email protected]" 
) 

case class Email(username: String, host: String) 
case class User(id: Int, email: Email) 


val idAndEMail = """^([^,]+),([^@]+)@(.+)$""".r 

и определить функцию, которая преобразует строку в качестве пользователя:

def lineToUserWithMail(line: String) : Option[User] = 
    idAndEMail.findFirstMatchIn(line) map { 
case userWithEmail(id,user,host) => User(id.toInt, Email(user,host)) 
    } 

Применение функции для всех линий

lines flatMap lineToUserWithMail 
//res29: List[User] = List(User(1,Email(alice,alice.com)), User(2,Email(bob,bob.com)), User(3,Email(carol,carol.com))) 

В качестве альтернативы вы можете реализовать пользовательские экстракторы в кластерах case, добавив метод «unapply». Но в этом случае это не стоило бы боли.

Ниже приведен пример для

Исключить
class Email(user:String, host:String) 
object Email { 
def unapply(s: String) : Option[(String,String)] = s.split("@") match { 
    case Array(user, host) => Some((user,host)) 
    case _ => None 
} 

}

"[email protected]" match { 
case Email(u,h) => println(s"$u , $h") 
} 
// prints bob , bob.com 

Слово предупреждения об использовании Regex для разбора CSV-данных. Это не так просто, как вы могли подумать, я бы рекомендовал использовать CSV-Reader как http://supercsv.sourceforge.net/, который обрабатывает некоторые неприятные кромки из коробки.

+0

Ну, я думаю, я задал неправильный вопрос, пытаясь представить себе самый простой пример. Структура пользователя содержит огромное количество полей, поэтому извлечение всего с помощью одного регулярного выражения недопустимо. Всегда можно просто получить доступ к массиву по индексу, но я бы предпочел использовать какой-то умный трюк с экстракторами. Кажется довольно сложным комбинировать их для чего-то вроде «1, Алиса, 21212, Балтимор, MD» -> Пользователь (1, Алиса, Адрес (21212, Балтимор, MD)) ' – synapse

+0

Хорошо, можно было бы определить несколько методы несанкционированного доступа к сопутствующим объектам. я добавлю пример. –

Смежные вопросы