Clojure:第一个空格分隔字符的印章

时间:2011-12-06 18:53:35

标签: string parsing filter clojure

我想解析并过滤一个如下所示的文件:

@@1 Row one. 
@@2 Row two.

我已经能够使用以下代码对行进行过滤:

(defn parse-text-cms [sel-row]
  (let [f_data  (st/split  #"@@" (slurp "cms/tb_cms.txt"))] 
  ;(prn (map #(take 1 %) f_data))))
  (filter  #(= (first (take 1 %)) sel-row) f_data)))

但是,这段代码给了我(如果sel-row = 1):

1 Row one.

我想切断那个1以及之后的空间,所以要:

Row one.

我认为有一些序列魔术可以做到这一点。我无法想出一个优雅的解决方案。

3 个答案:

答案 0 :(得分:2)

我会按以下方式定义函数:

(defn parse-text-cms [sel-row]
  (with-open [input (clojure.java.io/reader "cms/tb_cms.txt")]
    (first
     (for [[_ number line] (map (partial re-find #"@@(\d)+\s+(.*)")
                                (line-seq input))
           :when (= number (str sel-row))]
       line))))

line-seqreader的组合为我提供了输入文件中的一系列行。 with-open确保在完成后正确关闭文件。我将一个正则表达式应用于每行查找@@后跟一个数字和一些空格的行。

re-find返回包含三个项目的向量:

  • 整个匹配的行
  • 数字(正则表达式中的第一个组)
  • 该行的其余部分(正则表达式中的第二组)

我使用number语句中的解构将这些绑定到linefor(我对整个匹配的行不感兴趣,所以我忽略了这一点)。我使用sel-row过滤了所选:when,并仅生成line的其余部分。

由于我只希望文件中有一个匹配,所以我只返回for构建的序列中的第一个项目。由于formapline-seq的懒惰,这也会在找到项目后停止读取文件。

如果对行进行大量查找,我建议将整个文件加载到内存中,而不是每次都读取它。

答案 1 :(得分:1)

另一个解决方案是使用功能解析器库,例如dj-peg(我写的)。

https://github.com/bmillare/dj-peg

然后你可以这样写:

 (require '[dj-peg :as p])
 (let [line "@@1 the remaining line\n"
       initial (p/token #"@@\d+\s+)]
       (second (p/parse initial line)))

函数解析使用p / token返回的解析器来解析行中的文本。它将返回一个向量,第一个值作为解析的结果,第二个是剩余的输入。因此,如果我们调用第二个,我们将得到其余部分。运行它返回

 "the remaining line\n"

我建议您查看图书馆。它是用伪文化编程风格编写的,因此源代码读取非常流畅。在完成源代码之后,您应该能够理解解析模型。

答案 2 :(得分:0)

使用line-seq的先前给出的答案和正则表达式组的解构对于给定的用例很有效。

在一般情况下,你想要的只是字符串操作clojure.core包含subs函数。 http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/subs

subs是使用java interop和java String类的substring方法实现的。


user=> (subs "abcdef" 1)
"bcdef"
user=> (subs "abcdef" 2 4)
"cd"