请在 2024 年 Clojure 状态调查!中分享您的想法。

欢迎!请查看 关于 页面了解本站的工作原理。

0
ClojureScript

re-matches 函数不具备正确的语义:它对字符串进行搜索(而非匹配),如果字符串和匹配字符串不相等,将返回 nil。这不同于真正的匹配,后者将"^"和"$"插入到模式的首尾。

Clojure 示例

user=> (re-find #"0|[1-9][0-9]+|0[xX][0-9a-zA-Z]+" "0x1") "0" user=> (re-matches #"0|[1-9][0-9]+|0[xX][0-9a-zA-Z]+" "0x1") "0x1"

比较 ClojureScript

ClojureScript:cljs.user> (re-find #"0|[1-9][0-9]+|0[xX][0-9a-zA-Z]+" "0x1") "0" ClojureScript:cljs.user> (re-matches #"0|[1-9][0-9]+|0[xX][0-9a-zA-Z]+" "0x1") nil

这是 CLJS-775 问题(之一)的原因。

我不太清楚这里该如何处理。我首先想到的是,让 re-matches 检查其正则输入的 -source 属性,将字符串用 "^$" 封装起来,然后仔细地将所有标志复制到新正则表达式中。

提问
1. 有哪些有效的模式在这里可能不安全?也就是说,我们无法将 "^" 放在首个位置?"^^abc$$" 可以吗?
1. 如果 "^" 和 "$" 已经是模式的第一个和最后一个字符,我们能避免复制吗?
1. 多行模式在这方面如何处理?
1. regexinstance.lastIndex 是对正则实例的可变性(或在旧浏览器上的 RegExp 全局标志)的一部分,用于在相同字符串上多次调用 exec() 的字符串偏移量。如果 re-* 接收到设置了全局标志的正则表达式,我不知道该怎么办。(顺便说一下,这是拒绝 CLJS-150 的一个很好的理由:允许 clojure 接受全局标志会使得正则表达式对象具有状态,并将完全破坏 re-seq,例如。)

4 答案

0

由:favila 评论

我想提出一个有些激进的建议,该建议将:解决此问题和 CLJS-810,使我们可以更好地解决 CLJS-485、CLJS-746、CLJS-794(clojure.string/replace 的问题),并允许我们在 JavaScript 中的模式中添加一些正则表达式作为值的便利之处,并通过实现更多的 re-* 函数使 clojurescript 的正则表达式处理更接近 clojure。

在以下 cljsfiddle 中的实现示例(不是一个补丁):http://cljsfiddle.net/fiddle/favila.regexp

重要点

  1. 使用 re-pattern 创建一个 Pattern 对象,它提供了用于搜索 (re-find) 或精确匹配 (re-matches) 或重复搜索 (re-seq, re-matcher + re-find) 的方法。即使这些方法使用的正则表达式字符串相似,但在 JavaScript 中每个都必须是一个不同的 RegExp 对象。re-find 和 re-matches 模式可以被缓存。所有这些都可以延迟生成 RegExps。
  2. 正则表达式字面量会产生这些 Pattern 对象而不是 RegExp 对象。
  3. 创建一个 Matcher 对象以对应当前未实现的 re-matcher。它结合了一个具有全局标记的 RegExp 对象、一个搜索字符串和一个完成标记。如果它保留最后一个匹配(类似于 java),cljs 也可以实现 re-groups。
  4. 让 re-seq 使用 Matcher 对象和原生 RegExps 提供的全局匹配的 .lastIndex。因此,其实现不再需要在每次匹配后进行字符串切片。
  5. 如果 re-find 接收到一个原生 RegExp 对象而不是模式,它将直接使用它。这符合当前的行为。
  6. 如果 re-matches 接收到一个原生 RegExp 对象并且它不适合精确匹配,则从输入 RegExp 克隆一个新的 RegExp,并在其前后添加 ^ 和 $,并添加全局标记。此技术用于 clojure.string/replace,但使用不正确。

想法呢?

0

评论者:dnolen

这听起来很有趣,但我对互操作性有些担心。我想人们会期望函数既能接受常规的 RegExps,也能接受 Pattern。你在这里提到了这个问题吗?

0

由:favila 评论

如果我的某些想法比较模糊,请谅解;我有一段时间没想过这个问题了。

首先要注意,一组有限的 RegExps 是有效地 "纯" 的:如果它们执行完整的字符串匹配(例如,从 ^ 开始并以 $ 结束)并且全局标记设置为 false,则它们的 lastIndex 总是似乎为 0。

互操作性可能性

  • 可以从一个创建另一个,因此强制转换总是可行的。例如,re-pattern 可以接受 RegExp 和一些其他(clojure-specific)函数可以强制转换从 Pattern 或 Matcher 到 RegExp。或者在 re-matcher 返回一个与 RegExp 兼容的对象—请参阅下面的内容。
  • 传递给 cljs re-* 的 RegExp:"纯" 的正则表达式可以直接使用,否则我们将创建一个 Pattern 和/或 Matcher(我不记得具体细节了,但是 fiddle 应该涵盖了它们)。
  • 作为 RegExp 使用的 Pattern:Pattern 可以公开 RegExp 实例的所有属性。如果这个模式是纯的,它可以实现 .test 和 .exec。.lastIndex 总是会是 0。对于不纯的模式,我不确定该怎么办:抛出异常、依然表现得纯,还是返回一个新对象?
  • 作为 RegExp 使用的 Matcher:一个 Matcher 可以精确地复制 RegExp 实例,也许甚至使用同一个原型。像 RegExp 一样使用它将改变对象并扰乱其内部状态,但是只要它在任何时候都既用作 RegExp,又用作 Matcher,这就不会有问题。注意
    **** Matcher 在 Java 中保留匹配的字符串。JavaScript 信任你总是提供相同的字符串(例如,在 while 循环中)。
    • Java 的 Matcher 保留最后一个匹配(用于 re-groups)。JavaScript 的 RegExp 不保留。
      当Js的正则表达式中的lastIndex达到源字符串的末尾时,它将自动重置。而Java的Matcher不会。 Matcher必须是一个包装器,而不是一个普通的RegExp,因为这些额外的三个状态位。
      re-matcher的返回值只由re-find和re-groups的1参数形式消费。 re-seq可以在内部使用matcher,但由于它是私有的,因此不必使用。
    • 其他Matcher的Java方法应该实现吗?
  • String.prototype.match、 .replace、 .search和 .split传入的Pattern:我还没有想过这个问题。考虑因素
    **** 任何使用通过模式字面量或re-pattern创建的对象,并将其用作这些String方法参数的cljs代码都是问题代码。如果它们使用clojure.string方法代替,就不会有问题。
    • 这种代码在java clojure中也不可能实现:只有 (.split s "pattern-str") 在java/clj和js/cljs中是相同的,并且它将继续在两个平台上正常工作(不使用标志)。我们可能只需让人们修复这种代码,因为它是平台特定的,但需要看到这种问题的普遍程度。
      解决这种代码的方法可以是:
      使用我们将提供的pattern→regexp强制函数。
      直接使用js/RegExp构造那些正则表达式。 使用clojure.string函数而不是String方法。这也具有在clj和cljs之间可移植的优势。
    • 我们可能可以修补RegExp构造函数或干涉String原型链来实现pattern→regexp强制转换,但这是一个激进的解决方案。

如果RegExp.prototype.test、.exec和String.prototype.match、.replace、.search和.split的使用不多,那么在Clojure(java)代码中,使用Pattern和Match方法或直接使用String方法的极其不常见。大多数情况下,它们被视为用于re-*和clojure.string/**的透明对象。在相同风格的代码中编写的代码将不受影响,我们可以声明任何其他使用为平台特定的,并需要显式创建正则表达式(并不要让Matcher或Pattern表现得像正则表达式)。这是如果没有太多使用RegExp.prototype.test、.exec和String.prototype.match、.replace、.search和.split,我的首选的互操作方法。

0
by
参考:https://clojure.atlassian.net/browse/CLJS-776(由favila报告)
...