新手在Clojure中转换CSV文件

Bil*_*l_B 9 perl clojure

我对编程既新又老 - 主要是我在工作中编写了很多小的Perl脚本.当我想学习Lisp时,Clojure就出来了,所以我也在不知道Java的情况下学习Clojure.这很难,但到目前为止一直很有趣.

我已经看过几个类似问题的例子,但没有任何东西可以映射到我的问题空间.有没有规范的方法来提取Clojure中CSV文件的每一行的值列表?

这是一些实际工作的Perl代码; 包含非Perlers的评论:

# convert_survey_to_cartography.pl
open INFILE, "< coords.csv";       # Input format "Northing,Easting,Elevation,PointID"
open OUTFILE, "> coords.txt";      # Output format "PointID X Y Z".
while (<INFILE>) {                 # Read line by line; line bound to $_ as a string.
    chomp $_;                      # Strips out each line's <CR><LF> chars.
    @fields = split /,/, $_;       # Extract the line's field values into a list.
    $y = $fields[0];               # y = Northing
    $x = $fields[1];               # x = Easting
    $z = $fields[2];               # z = Elevation
    $p = $fields[3];               # p = PointID
    print OUTFILE "$p $x $y $z\n"  # New file, changed field order, different delimiter.
}
Run Code Online (Sandbox Code Playgroud)

我在Clojure中有点困惑,并尝试以一种命令式的方式将它拼凑在一起:

; convert-survey-to-cartography.clj
(use 'clojure.contrib.duck-streams)
(let
   [infile "coords.csv" outfile "coords.txt"]
   (with-open [rdr (reader infile)]
     (def coord (line-seq rdr))
     ( ...then a miracle occurs... )
     (write-lines outfile ":x :y :z :p")))
Run Code Online (Sandbox Code Playgroud)

我不希望最后一行实际工作,但它得到了重点.我正在寻找以下内容:

(def values (interleave (:p :y :x :z) (re-split #"," coord)))
Run Code Online (Sandbox Code Playgroud)

比尔,谢谢

kot*_*rak 15

请不要使用嵌套的def.它没有,你认为它做什么.def始终是全球性的!对于当地人来说,请改用.虽然库函数很难知道,但这里有一个版本,通常编写函数式编程的一些特性,特别是clojure.

(import 'java.io.FileWriter 'java.io.FileReader 'java.io.BufferedReader)

(defn translate-coords
Run Code Online (Sandbox Code Playgroud)

可以通过(doc translate-coords)在REPL中查询文档字符串.工作例如.适用于所有核心功能.因此提供一个是个好主意.

  "Reads coordinates from infile, translates them with the given
  translator and writes the result to outfile."
Run Code Online (Sandbox Code Playgroud)

翻译器是一个(可能是匿名的)函数,它从周围的样板中提取翻译.因此,我们可以使用不同的转换规则重用此函数.这里的类型提示避免构造函数的反射.

  [translator #^String infile #^String outfile]
Run Code Online (Sandbox Code Playgroud)

打开文件.with-open将保证文件在其主体离开时关闭.无论是通过正常的"从底部掉下来"还是通过抛出的异常.

  (with-open [in  (BufferedReader. (FileReader. infile))
              out (FileWriter. outfile)]
Run Code Online (Sandbox Code Playgroud)

我们将*out*流临时绑定到输出文件.因此绑定中的任何打印都将打印到文件中.

    (binding [*out* out]
Run Code Online (Sandbox Code Playgroud)

map方法:取序列和给定函数应用于每个元素并返回结果的序列.这#()是匿名函数的简写符号.它需要一个参数,填充在%.这doseq基本上是输入的循环.因为我们为副作用(即打印到文件)这样做,所以doseq是正确的构造.经验法则:maplazy => for result,doseq:eager => for side effects.

      (doseq [coords (map #(.split % ",") (line-seq in))]
Run Code Online (Sandbox Code Playgroud)

println照顾\n到最后一行.interpose获取seq并在其元素之间添加第一个参数(在我们的例子中为"").(apply str [1 2 3])等效于(str 1 2 3)动态构造函数调用并且很有用.这->>是一个相对较新的clojure宏,它有助于提高可读性.它表示"接受第一个参数并将其作为最后一项添加到函数调用中".给定->>的相当于:(println (apply str (interpose " " (translator coords)))).(编辑:另一个注意事项:因为分隔符是\space,我们在这里也可以这样写(apply println (translator coords)),但是interpose版本允许像我们对翻译器功能一样参数化分隔符,而短版本会硬连线\space.)

        (->> (translator coords)
          (interpose " ")
          (apply str)
          println)))))

(defn survey->cartography-format
  "Translate coords in survey format to cartography format."
Run Code Online (Sandbox Code Playgroud)

这里我们使用解构(注意双[[]]).这意味着函数的参数可以转换为seq,例如.矢量或列表.将第一个元素绑定到第二个元素y,x依此类推.

  [[y x z p]]
  [p x y z])

(translate-coords survey->cartography-format "survey_coords.txt" "cartography_coords.txt")
Run Code Online (Sandbox Code Playgroud)

这里再次不那么波动:

(import 'java.io.FileWriter 'java.io.FileReader 'java.io.BufferedReader)

(defn translate-coords
  "Reads coordinates from infile, translates them with the given
  translator and writes the result to outfile."
  [translator #^String infile #^String outfile]
  (with-open [in  (BufferedReader. (FileReader. infile))
              out (FileWriter. outfile)]
    (binding [*out* out]
      (doseq [coords (map #(.split % ",") (line-seq in))]
        (->> (translator coords)
          (interpose " ")
          (apply str)
          println)))))

(defn survey->cartography-format
  "Translate coords in survey format to cartography format."
  [[y x z p]]
  [p x y z])

(translate-coords survey->cartography-format "survey_coords.txt" "cartography_coords.txt")
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

编辑:对于CSV阅读,您可能需要类似OpenCSV的内容.


Bri*_*per 8

这是一种方式:

(use '(clojure.contrib duck-streams str-utils))                 ;;'
(with-out-writer "coords.txt"
  (doseq [line (read-lines "coords.csv")]
    (let [[x y z p] (re-split #"," line)]
      (println (str-join \space [p x y z])))))
Run Code Online (Sandbox Code Playgroud)

with-out-writer绑定*out*使得您打印的所有内容都将转到您指定的文件名或流,而不是标准输出.

使用def它并不是惯用的.更好的方法是使用let.我正在使用解构来将每行的4个字段分配给4行let名称; 那么你可以用那些做你想做的事.

如果你为了副作用(例如I/O)而迭代某些东西,你通常应该去doseq.如果你想将每一行收集到一个哈希映射中并稍后用它们做一些事情,你可以使用for:

(with-out-writer "coords.txt"
  (for [line (read-lines "coords.csv")]
    (let [fields (re-split #"," line)]
      (zipmap [:x :y :z :p] fields))))
Run Code Online (Sandbox Code Playgroud)