提问人:Elías 提问时间:10/12/2023 最后编辑:Elías 更新时间:10/13/2023 访问量:78
使用 clojure.string/split 时的 ClassCastException
ClassCastException when using clojure.string/split
问:
我正在编写一个网站,将 clojure hiccup 用于 html,使用 clojure garden 用于 css。现在我在使用 clojure.string/split 拆分文件名路径时遇到了问题。所以,我想要.some/path/file.md
file
对于上下文,这里是代码片段,问题出在哪里
(defn blog-card [post]
[:a.blog-post-card-link {:href (->> (:location post)
(string/split #"/")
last
(string/replace ".md" ""))}
[:div.blog-post-card
-- rest of content -- ]])
现在,此处调用了此函数
(defn blog-content []
[:div.blog-content
[:div.blog-post-cards-list
(let [json-str (slurp "resources/public/markdown/posts/all-posts.json")
posts (json/parse-string json-str true)]
(map blog-card (reverse posts)))]])
哪里是和是.看起来像这样json
[cheshire.core :as json]
string
[clojure.string :as string]
all-posts.json
[
{
"id": "0",
"title": "Test article #1",
"description": "This article is just a place-filler for development.",
"image": "images/mountain.jpg",
"date": "10 Oct 2023",
"author": "Elías Hauksson",
"location": "markdown/posts/test-post01.md"
},
-- more test entries --
]
因此,通过这个例子,我想实现的是,当按下卡片时,人们会被重定向到/test-post01
现在,当我尝试运行此代码时,收到以下错误消息
HTTP ERROR 500 java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.regex.Pattern (java.lang.String and java.util.regex.Pattern are in module java.base of loader 'bootstrap')
URI: /blog
STATUS: 500
MESSAGE: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.regex.Pattern (java.lang.String and java.util.regex.Pattern are in module java.base of loader 'bootstrap')
SERVLET: -
CAUSED BY: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.regex.Pattern (java.lang.String and java.util.regex.Pattern are in module java.base of loader 'bootstrap')
Caused by:
java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.regex.Pattern (java.lang.String and java.util.regex.Pattern are in module java.base of loader 'bootstrap')
at clojure.string$split.invokeStatic(string.clj:219)
at clojure.string$split.invoke(string.clj:219)
at eliashaukssoncom.views.blog$blog_card.invokeStatic(blog.clj:10)
at eliashaukssoncom.views.blog$blog_card.invoke(blog.clj:8)
at clojure.core$map$fn__5935.invoke(core.clj:2772)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:51)
at clojure.lang.RT.seq(RT.java:535)
at clojure.core$seq__5467.invokeStatic(core.clj:139)
at clojure.core$map$fn__5935.invoke(core.clj:2763)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:51)
at clojure.lang.RT.seq(RT.java:535)
at clojure.core$seq__5467.invokeStatic(core.clj:139)
at clojure.core$apply.invokeStatic(core.clj:662)
at clojure.core$apply.invoke(core.clj:662)
at hiccup.compiler$fn__3080.invokeStatic(compiler.clj:139)
at hiccup.compiler$fn__3080.invoke(compiler.clj:133)
at hiccup.compiler$fn__3063$G__3058__3068.invoke(compiler.clj:119)
at clojure.core$map$fn__5935.invoke(core.clj:2770)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:51)
at clojure.lang.RT.seq(RT.java:535)
at clojure.core$seq__5467.invokeStatic(core.clj:139)
at clojure.core$apply.invokeStatic(core.clj:662)
at clojure.core$apply.invoke(core.clj:662)
at hiccup.compiler$fn__3080.invokeStatic(compiler.clj:139)
at hiccup.compiler$fn__3080.invoke(compiler.clj:133)
at hiccup.compiler$fn__3063$G__3058__3068.invoke(compiler.clj:119)
at hiccup.compiler$render_element.invokeStatic(compiler.clj:129)
at hiccup.compiler$render_element.invoke(compiler.clj:123)
at hiccup.compiler$fn__3078.invokeStatic(compiler.clj:136)
at hiccup.compiler$fn__3078.invoke(compiler.clj:133)
at hiccup.compiler$fn__3063$G__3058__3068.invoke(compiler.clj:119)
at clojure.core$map$fn__5935.invoke(core.clj:2770)
at clojure.lang.LazySeq.sval(LazySeq.java:42)
at clojure.lang.LazySeq.seq(LazySeq.java:51)
at clojure.lang.RT.seq(RT.java:535)
at clojure.core$seq__5467.invokeStatic(core.clj:139)
at clojure.core$apply.invokeStatic(core.clj:662)
at clojure.core$apply.invoke(core.clj:662)
at hiccup.compiler$fn__3080.invokeStatic(compiler.clj:139)
at hiccup.compiler$fn__3080.invoke(compiler.clj:133)
at hiccup.compiler$fn__3063$G__3058__3068.invoke(compiler.clj:119)
at hiccup.compiler$render_element.invokeStatic(compiler.clj:129)
at hiccup.compiler$render_element.invoke(compiler.clj:123)
at hiccup.compiler$fn__3078.invokeStatic(compiler.clj:136)
at hiccup.compiler$fn__3078.invoke(compiler.clj:133)
at hiccup.compiler$fn__3063$G__3058__3068.invoke(compiler.clj:119)
at eliashaukssoncom.views.base$base$fn__1001.invoke(base.clj:26)
at eliashaukssoncom.views.base$base.invokeStatic(base.clj:26)
at eliashaukssoncom.views.base$base.invoke(base.clj:25)
at eliashaukssoncom.views.blog$blog_page.invokeStatic(blog.clj:35)
at eliashaukssoncom.views.blog$blog_page.invoke(blog.clj:34)
at eliashaukssoncom.handler$fn__1026.invokeStatic(handler.clj:13)
at eliashaukssoncom.handler$fn__1026.invoke(handler.clj:13)
at compojure.core$wrap_response$fn__2214.invoke(core.clj:158)
at compojure.core$wrap_route_middleware$fn__2198.invoke(core.clj:128)
at compojure.core$wrap_route_info$fn__2203.invoke(core.clj:137)
at compojure.core$wrap_route_matches$fn__2207.invoke(core.clj:146)
at compojure.core$routing$fn__2222.invoke(core.clj:185)
at clojure.core$some.invokeStatic(core.clj:2718)
at clojure.core$some.invoke(core.clj:2709)
at compojure.core$routing.invokeStatic(core.clj:185)
at compojure.core$routing.doInvoke(core.clj:182)
at clojure.lang.RestFn.applyTo(RestFn.java:139)
at clojure.core$apply.invokeStatic(core.clj:669)
at clojure.core$apply.invoke(core.clj:662)
at compojure.core$routes$fn__2226.invoke(core.clj:192)
at ring.middleware.resource$wrap_resource_prefer_resources$fn__596.invoke(resource.clj:25)
at clojure.lang.Var.invoke(Var.java:384)
at ring.adapter.jetty$proxy_handler$fn__420.invoke(jetty.clj:27)
at ring.adapter.jetty.proxy$org.eclipse.jetty.server.handler.AbstractHandler$ff19274a.handle(Unknown Source)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
at org.eclipse.jetty.server.Server.handle(Server.java:516)
at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034)
at java.base/java.lang.Thread.run(Thread.java:829)
我试过什么
- 替换为
(:location post)
"some/test/path/file.md"
- 编写不带运算符的函数。喜欢这个
->>
{:href (string/replace (last (string/split (:location post) #"/")) #".md" "")}
编辑:第 2 点被证明是正确的解决方案(如@Eugene_Pakhomov所提到的),这是我代码中的一个错别字,第一次没有让它工作。
提前感谢您的建议。
答:
clojure.string/split
接受字符串作为第一个参数,接受正则表达式作为第二个参数。“我尝试过什么”部分中的代码应该可以工作。main 部分中的代码不起作用,因为已展开为 .(->> (:location post) (string/split #"/"))
(string/split #"/" (:location post))
评论
作为替代方案,您也可以直接使用正则表达式执行此操作,例如:re-find
(let [post {:location "/some/path/here/foobar.md"}]
(re-find #"([^/.]+).md" (:location post)))
=> ["foobar.md" "foobar"]
正则表达式说:
(
- 打开 Paren,在正则表达式中启动一个捕获组[^/.]+
- 匹配除(斜杠或句点)以外的任何字符一次或多次()。/
.
+
)
- 关闭捕获组.md
- 期望捕获的组后面跟着.md
返回的向量包含完全匹配,后跟捕获的组,这就是您所追求的。"foobar.md"
"foobar"
如果要使用线程宏 (),这可能如下所示:->>
(let [post {:location "/asd/adsf/asdf/foobar.md"}]
(->> (:location post)
(re-find #"([^/.]+).md" )
second))
=> "foobar"
或者,如果你想更简洁,你可以使用正则表达式环视断言:
(re-find #"[^/.]+(?=\.md)" "/asd/adsf/asdf/foobar.md")
=> "foobar"
其中,该部分是“零宽度正向前看”。这实质上意味着“匹配一个由 / 或 .后跟字符串“.md”,但不要使“.md”成为匹配的一部分”。有关 clojure 中正则表达式实现的文档可以在 java 模式类 javadocs 中找到,因为 clojure 使用 JVM 模式实现。(?=...)
应该注意的是,对于您的初始示例,如果路径不以 结尾,则匹配将仅返回,与“文件名”部分不匹配。如果需要任意文件扩展名,可以执行如下操作:.md
nil
(re-find #"[^/.]+(?=\..+)" "/asd/adsf/asdf/foobar.orange")
=> "foobar"
对于文件系统名称和路径的使用,最好使用软件包的内置功能。在这种情况下,无论操作系统如何,该类都可以安全、轻松地解析出路径名段。java.io
File
我会这样解决它:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str])
(:import
[java.io File]))
(verify
(let [file-obj (File. "some/path/file.md")
name-str (.getName file-obj)
name-root (first (str/split name-str #"\."))]
(is= "file.md" name-str)
(is= "file" name-root)))
请注意,假设文件名中只有一个(“点”)字符!为了获得健壮的代码,请添加错误检查!(first (str/split ...))
.
评论