我想解析并过滤一个如下所示的文件:
@@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.
我认为有一些序列魔术可以做到这一点。我无法想出一个优雅的解决方案。
答案 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-seq
和reader
的组合为我提供了输入文件中的一系列行。 with-open
确保在完成后正确关闭文件。我将一个正则表达式应用于每行查找@@
后跟一个数字和一些空格的行。
re-find
返回包含三个项目的向量:
我使用number
语句中的解构将这些绑定到line
和for
(我对整个匹配的行不感兴趣,所以我忽略了这一点)。我使用sel-row
过滤了所选:when
,并仅生成line
的其余部分。
由于我只希望文件中有一个匹配,所以我只返回for
构建的序列中的第一个项目。由于for
,map
和line-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"