9
votes

Accès fortement tapé à CSV à Scala?

J'aimerais accéder aux fichiers CSV à Scala de manière fortement dactylographiée. Par exemple, comme je lis chaque ligne du CSV, il est automatiquement analysé et représenté comme un tuple avec les types appropriés. Je pourrais spécifier les types à l'avance dans une sorte de schéma transmis à l'analyseur. Y a-t-il des bibliothèques qui existent pour ce faire cela? Sinon, comment pourrais-je continuer à mettre en œuvre cette fonctionnalité par moi-même?


1 commentaires

En raison de tant de problèmes, même en présence d'une RFC pour le type MIME .CSV, je vous suggère fortement d'utiliser une bibliothèque de scala native de RFC bien entretenue qui gère de manière optimale ce problème, kantan.csv: NRINAUDO.GITUB.IO/KANTAN.CSV


7 Réponses :


-2
votes

Si vous connaissez le # et les types de champs, peut-être comme ça ?:

case class Friend(id: Int, name: String) // 1,  Fred

val friends = scala.io.Source.fromFile("friends.csv").getLines.map { line =>
   val fields = line.split(',')
   Friend(fields(0).toInt, fields(1))
}


0 commentaires

1
votes

Edit: Comme indiqué dans un commentaire, kantan.csv (voir autre réponse) est probablement le meilleur à compter du temps que j'ai fait cette modification (2020-09-03).

Ceci est fait plus compliqué que cela devrait en raison des règles de citation non séventielles pour le CSV. Vous devriez probablement commencer par un analyseur CSV existant, par ex. OpenCsv ou l'un des projets appelés SCALA-CSV. (Il y a à moins trois .) P>

Ensuite, vous vous retrouvez avec une sorte de collection de collections de chaînes. Si vous n'avez pas besoin de lire rapidement des fichiers CSV massifs, vous pouvez simplement essayer d'analyser chaque ligne dans chacun de vos types et de prendre le premier qui ne jette pas une exception. Par exemple, P>

import scala.util._

case class Person(first: String, last: String, age: Int) {}
object Person {
  def fromCSV(xs: Seq[String]) = Try(xs match {
    case s0 +: s1 +: s2 +: more => new Person(s0, s1, s2.toInt)
  })
}


1 commentaires

Je vous suggère fortement d'utiliser une bibliothèque native de Scala de RFC bien entretenue qui gère de manière optimale ce problème, kantan.csv: nrineudo.github.io/kantan.csv



2
votes

Si votre contenu a des citations doubles pour enfermer d'autres doubles citations, des virgules et des nouvelles lignes, j'utiliserais certainement une bibliothèque comme opencsv qui traite correctement avec des caractères spéciaux. Typiquement, vous vous retrouvez avec itérateur [tableau [stress]] . Ensuite, vous utilisez itérator.map ou Collecte pour transformer chaque tableau [string] dans vos tuples traitant des erreurs de conversions de type. Si vous devez faire traiter l'entrée sans chargement de toutes en mémoire, vous continuez ensuite à fonctionner avec l'itérateur, sinon vous pouvez convertir en une liste vecteur ou list et fermez le flux d'entrée. .

Il peut ressembler à ceci: xxx

Selon la façon dont vous avez besoin de faire affaire avec des erreurs, vous pouvez retourner gauche pour les erreurs et Droite Pour Success Nuples pour séparer les erreurs des lignes correctes. De plus, j'enveloppe parfois de tout cela en utilisant Scala-bras pour la fermeture des ressources. Donc, mes données sont peut-être enveloppées dans la ressource managedResource monade afin que je puisse utiliser l'entrée provenant de plusieurs fichiers.

Enfin, bien que vous souhaitiez travailler avec des tuples, j'ai trouvé que Il est généralement plus clair d'avoir une classe de cas appropriée pour le problème, puis écrit une méthode qui crée cet objet de classe de cas à partir d'un tableau [string] .


2 commentaires

Quels sont les avantages de l'utilisation de la classe de cas?


Il donne des noms aux champs tels que personne (nom: chaîne, âge: int) . Donc, plus tard, lorsque vous devez y accéder, vous pouvez faire p.name plutôt que t._1 . Cela fonctionne bien par exemple dans rows.sortby (_. Nom)



0
votes

J'ai construit ma propre idée de typasser fortement le produit final, plus que la scène de lecture elle-même..qui, comme indiqué comme indiqué pourrait être mieux traitée comme une étape une avec quelque chose comme Apache CSV, et la phase 2 pourrait être ce que j'ai fait . Voici le code que vous êtes le bienvenu. L'idée est de taper le CSVREADER [T] avec le type T .. Lors de la construction, vous devez fournir au lecteur avec un objet facteur de type [T]. L'idée ici est que la classe elle-même (ou dans mon exemple un objet d'assistance) décide des détails de la construction et découle ainsi cela de la lecture réelle. Vous pouvez utiliser des objets implicites pour passer l'aide de l'aide, mais je n'ai pas fait cela ici. Le seul inconvénient est que chaque ligne du CSV doit être du même type de classe, mais vous pouvez élargir ce concept au besoin.

import java.io._


    class CsvWriter[T] (factory:CsvFactory[T], fname:String, delim:String, append:Boolean = false) {

      private val out   = new PrintWriter(new BufferedWriter(new FileWriter(fname,append)));
      if (!append)  out.println(factory.header mkString delim )

      def flush() = out.flush()


      def println(s:String) =    out.println(s)

      def printObj(obj:T) =  println( factory makeRow(obj) mkString(delim) )
      def printAll(objects:Seq[T]) = objects.foreach(printObj(_))
      def close() = out.close

    }


0 commentaires

12
votes

Collections de produits semble être un bon ajustement pour vos besoins:

scala> val data = CsvParser[String,Int,Double].parseFile("sample.csv")
data: com.github.marklister.collections.immutable.CollSeq3[String,Int,Double] = 
CollSeq((Jan,10,22.33),
        (Feb,20,44.2),
        (Mar,25,55.1))


4 commentaires

J'aime la façon dont ça regarde, mais j'essaie de comprendre comment ça marche. Je ne comprends pas vraiment ce qui se passe dans csvparser.scala.template. Quels sont ces modèles dans la section de la batterie?


Le modèle est traité par Github.com/markLister/sbt-boLerPlate qui génère un fichier scala. Si vous construisez le projet, vous pouvez voir les résultats dans le répertoire cible / SCALA-2.10 / SRC_Managé. C'est exactement la même chose que Scala est propre tuple1 ... Tuple22 semble fonctionner. Les csvarsers existent pour les arités 1 à 22 et le compilateur sélectionne le bon pour la signature de type (schéma) que vous fournissez.


Existe-t-il une façon recommandée de traiter des valeurs nulelles ou des valeurs qui échouent à analyser? Ce serait bien si je pouvais donner l'option [t] comme paramètre de type; Cela pourrait essayer de l'analyser comme un T et de donner une personne si elle n'a pas manqué d'analyser ou d'être vide.


J'ai ajouté une section à la Readme.md intitulée "Récupération des erreurs d'analyse de terrain". Il y a un exemple d'analyse d'une option [int]] là. Vous pouvez soumettre une demande de traction pour prendre en charge ces convertisseurs. Voir Fichier GeneralConverter.scala à SRC / Main / SCALA / IO



1
votes

J'ai créé une aide de CSV fortement typée pour Scala, appelée objet-csv . Ce n'est pas un cadre à part entière, mais il peut être ajusté facilement. Avec vous, vous pouvez faire ceci:

case class Person (name: String, age: Int, salary: Double, isNice:Boolean = false)


1 commentaires

En raison de tant de problèmes, même en présence d'une RFC pour le type MIME .CSV, je vous suggère fortement d'utiliser une bibliothèque de scala native de RFC bien entretenue qui gère de manière optimale ce problème, kantan.csv: NRINAUDO.GITUB.IO/KANTAN.CSV



2
votes

Vous pouvez utiliser kantan.csv , conçu avec précision ce but à l'esprit.

Imaginez que vous avez l'entrée suivante: p> xxx pré>

à l'aide de kantan.csv, vous pouvez écrire le code suivant pour analyser: P>

import kantan.csv.ops._

new File("path/to/csv").asUnsafeCsvRows[(Int, String, Either[Float, Boolean])](',', false)


1 commentaires

Après avoir à naviguer dans cet espace plusieurs fois, je suis extrêmement heureux que vous ayez généré une solution adhéreuse RFC 4180! TYSVM! Outils.ietf.org/html/rfc4180