使用 clojure.string/split 时的 ClassCastException

ClassCastException when using clojure.string/split

提问人:Elías 提问时间:10/12/2023 最后编辑:Elías 更新时间:10/13/2023 访问量:78

问:

我正在编写一个网站,将 clojure hiccup 用于 html,使用 clojure garden 用于 css。现在我在使用 clojure.string/split 拆分文件名路径时遇到了问题。所以,我想要.some/path/file.mdfile

对于上下文,这里是代码片段,问题出在哪里

(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)

我试过什么

  1. 替换为(:location post)"some/test/path/file.md"
  2. 编写不带运算符的函数。喜欢这个->>
{:href (string/replace (last (string/split (:location post) #"/")) #".md" "")}

编辑:第 2 点被证明是正确的解决方案(如@Eugene_Pakhomov所提到的),这是我代码中的一个错别字,第一次没有让它工作。

提前感谢您的建议。

Clojure 类CastException 打嗝

评论


答:

4赞 Eugene Pakhomov 10/12/2023 #1

clojure.string/split接受字符串作为第一个参数,接受正则表达式作为第二个参数。“我尝试过什么”部分中的代码应该可以工作。main 部分中的代码不起作用,因为已展开为 .(->> (:location post) (string/split #"/"))(string/split #"/" (:location post))

评论

0赞 Elías 10/12/2023
是的,你是对的!!我的代码中有错别字。谢谢。
1赞 Matias Bjarland 10/12/2023 #2

作为替代方案,您也可以直接使用正则表达式执行此操作,例如: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 模式实现。(?=...)

应该注意的是,对于您的初始示例,如果路径不以 结尾,则匹配将仅返回,与“文件名”部分不匹配。如果需要任意文件扩展名,可以执行如下操作:.mdnil

(re-find #"[^/.]+(?=\..+)" "/asd/adsf/asdf/foobar.orange")

=> "foobar"
1赞 Alan Thompson 10/13/2023 #3

对于文件系统名称和路径的使用,最好使用软件包的内置功能。在这种情况下,无论操作系统如何,该类都可以安全、轻松地解析出路径名段。java.ioFile

我会这样解决它:

(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 ...)).