2013-09-29 45 views
0

我正在嘗試讀取CSV並將其存儲在陣列對象的矩陣狀數組中。我碰到的一個障礙是,字符串在中被讀出 - 也就是說,字符串「price」不僅僅是價格,而是scala中的「」「」price「」「」。因此,我想刪除那些周圍的引號。我也想確保任何數值被強制爲Double/Int,因爲它們是以字符串形式讀入的。格式化讀入CSV數據

我現在擁有的一切:

val rawObs = io.Source.fromFile(file).getLines() .map(_.split(",")).toArray 

// An example element of the array is: 
//scala> rawObs(2) 
//res93: Array[String] = Array("3", 0, "2013-02-27", 1, 52, 52, 1, "1", "kg") 

// Here, I make a function to remove surrounding strings and return a string, or if there are 
// not surrounding strings, return a Double. 
def fixRawObs(x: String) = { 
    // if it was actually meant to be a string: 
    if(x.startsWith(""""""")){ 
       // delete any " quotes 
       var y = x.replaceAll(""""""", "") 
      } else { // this means that x needs to be coerced to Int or Double 
       var y = x.toDouble 
      } 
      y // return y 
} 
// but this won't compile, it returns <console>:14: error: not found: value y 

// If it did compile, I'd want to do something like this: 
rawObs.map(_.map(fixRawObs(_))) 
// (although, is there a better way?) 

所以,基本上,我的第一個問題是如何解決我fixRawObs功能,其次,這是甚至一個好辦法做到這一點,還是有一些更好的方式來完成我想要的?我在做什麼感覺有點ha。。

我是超級新來的斯卡拉,所以它將不勝感激,如果答案不承擔太多的知識。謝謝!

回答

1

有幾個問題與您的代碼:

  1. 您正在嘗試存儲字符串和雙打在數組中。由於最接近的常用超級類型的字符串和雙精度是任意的,您將擁有一個數組[任何]。使用數組[任何]時,只要您想使用它們,就需要將它們作爲字符串或雙精度來轉換,這不是可取的。

  2. 您的函數fixRawObs()不編譯,因爲它試圖返回一個不可訪問的變量。 「y」在花括號內部被聲明,這使得它在花括號之外無法訪問。 「y」實際上甚至沒有必要,因爲Scala中的if語句返回一個值,就像函數一樣。你可以這樣做:

    def fixRawObs(x: String) = { 
        if(x.startsWith(""""""")) x.replaceAll(""""""", "") 
        else x.toDouble 
    }

這個函數的返回值類型是「任意」,雖然如此,你仍然必須手動轉換返回值的類型正確。再次,這不是一個好方法。

我推薦創建一個類,以便您可以擁有一個自定義數據結構,該結構使用適當的類型引用您的值。

case class Row(
    col1: String, col2: Double, col3: String, col4: Double, 
    col5: Double, col6: Double, col7: Double, col8: String, col9: String 
) 

如果您用適當的描述性名稱重新命名這些值將是最好的。

然後,您可以創建這樣的行對象:

def stripQuotes(s: String): String = { 
    if(s.startsWith("\"") && s.endsWith("\"")) s.dropRight(1).dropLeft(1) 
    else s 
} 

val csv = io.Source.fromFile(file) 
val rows = (for { 
    line <- file.getLines 
    s = line.split(",") 
    if(s.size == 9) 
} yield { 
    new Row(
     stripQuotes(s(0)), 
     s(1).toDouble, 
     stripQuotes(s(2)), 
     s(3).toDouble, 
     s(4).toDouble, 
     s(5).toDouble, 
     s(6).toDouble, 
     stripQuotes(s(7)), 
     stripQuotes(s(8)) 
    ) 
}).toArray 
csv.close() 
+0

謝謝!信息豐富,非常有幫助。唯一讓這個有點不理想的是CSV類型(String,Double,String,...)被硬編碼,所以如果CSV的格式略有改變,這將會中斷。 –

1

您可能想要使用一個解析CSV文件的庫,而不是嘗試自己完成邊緣案例。 Scala/Java有很多選項(onetwo)。

如果你在練習Scala,我會解釋它爲什麼不能編譯。問題在於你試圖返回y,這是在你的循環範圍內定義的,並且在其外部不可用。

在Scala中,函數的最後一個語句是返回值。因此,使您的if語句成爲函數中的最後一個語句,並立即返回替換/解析的值將執行您想要的操作。

def fixRawObs(x: String) = { 
    x.startsWith("\"") match { 
     case true => 
     x.replaceAll("\"", "") 
     case false => 
     x.toDouble 
    } 
} 

注意,函數將返回Any實例 - Scala中的所有類的超類。這是因爲你在一個子句中返回一個字符串,在另一個子句中返回一個Double。

瞭解數據的特定格式(例如,給定字段總是雙精度型或總是字符串),您可以將其重寫爲更精確並支持實際類型。

0

這個簡單的庫:product-collections做類似你正在嘗試與你的數組的數組做一些事情。它也有一個直觀的csv閱讀器。

更新:圖書館現在直接處理案件類別:

val csv = Array("3", 0, "2013-02-27", 1, 52, 52, 1, "1", "kg").mkString(",") 
csv: String = 3,0,2013-02-27,1,52,52,1,1,kg 

scala> case class Row(
    |  col1: String, col2: Double, col3: String, col4: Double, 
    |  col5: Double, col6: Double, col7: Double, col8: String, col9: String 
    |) 
defined class Row 

scala> CsvParser(Row).parse(new java.io.StringReader(csv)) 
res33: Seq[Row] = List(Row(3,0.0,2013-02-27,1.0,52.0,52.0,1.0,1,kg))