请分享您的想法,参加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 祸事)的能力、允许我们为 js 中的模式添加一些正则表达式作为值的优点,并通过实现更多 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. 创建一个与尚未实施的re-matcher对应的Matcher对象。它结合了一个全局标志RegExp对象、一个搜索字符串和一个完成标志。如果它保留最后一个匹配(类似于Java),ClojureScript也可以实现re-groups。
  4. 使re-seq使用Matcher对象,从而使用本土正则表达式提供的.lastIndex进行全局匹配。(它的实现不再需要在每次匹配后进行字符串切片。)
  5. 如果re-find给定了正则表达式对象而不是模式,它将直接使用它。这与当前的行为匹配。
  6. 如果re-matches给定了正则表达式对象并且它不适合精确匹配,则从一个带有^和$前缀和后缀的新RegExp克隆并添加全局标志。(这种技术用于clojure.string/replace中,但不正确。)

有什么想法吗?

0

由dnolen发表的评论:

这个听起来很有趣,但我对互操作性有些担忧。我认为人们会期望函数能够接受正则表达式以及Pattern。您在这里没有提到这个问题吗?

0

评论由:favila 添加

如果我的某些想法比较含糊,那是因为我有一段时间没有考虑这个问题了。

首先要注意,一小类RegExps实际上是“纯”的:如果它们执行全文匹配(例如,以^开始并以$结束)并且将全局标志设置为false,那么它们的lastIndex始终会显示为0。

互操作性可能性

  • Pattern和RegExp可以相互创建,所以强制转换始终是一个选项。例如,re-pattern可以接受一个RegExp和某些其他(ClojureScript特有的)函数可以强制转换来自Pattern或Matcher到RegExp。(或者可能是re-matcher可以返回一个与RegExp兼容的对象--见下文。)
  • 给cljs re-*的RegExp:"纯"正则表达式可以直接使用,否则我们创建一个Pattern和/或Matcher。(我不记得具体细节,但是fiddle应该涵盖了它们。)
  • 用作RegExp的模式:模式可以公开RegExp实例的所有属性。如果模式是纯的,它可以实现.test和.exec。.lastIndex始终是0。对于不纯的模式不知所措:抛出异常,仍然表现得像纯模式,返回一个新的对象?
  • 用作RegExp的Matcher:Matcher可以准确地复制RegExp实例,甚至可能使用相同的原型。将其用作RegExp将更改对象并干扰其内部状态,但是只要它要么始终用作RegExp,要么始终用作Matcher,这都不会有问题。备注
    **** Matcher在Java中保留了匹配的字符串。JavaScript信任您始终提供相同的字符串(例如,在while循环中)。
    • Java 的 Matcher 保存最后的匹配结果(用于模式分组)。JavaScript 的 RegExp 没有这个功能。
      当 lastIndex 达到源字符串末尾时,JavaScript 的 RegExp 会自动重置。Java 的 Matcher 则不会。 由于这三个额外的状态,Matcher 必须是一个包装器,而不是一个正常的 RegExp。
      正则表达式匹配器 re-matcher 的返回值仅由 re-find 和 re-groups 的 1 参数形式消耗。 re-seq 可以在内部使用匹配器,但由于它是私有的,因此不需要。
    • 应该实现其他 Matcher 的 Java 方法吗?
  • 传递给 String.prototype.match、.replace、.search 和 .split 的模式:我还没想过这个问题。考虑因素
    **** 任何使用通过模式文字或 re-pattern 创建的对象且将其作为这些 String 方法参数的 cljs 代码都是问题代码。如果它们使用 clojure.string 方法,那么就没有问题。
    • 这样的代码在 java cljs 中也无法实现:只有 (.split s "pattern-str") 在 java/clj 和 js/cljs 中相同,并且将在两个平台上继续工作(不使用标志)。我们可能只能让人们修复这样的代码,因为它具有平台特异性,但我要看看这有多普遍。
      修复此类代码的方法是:
      使用我们将提供的 pattern→regexp 诱导函数。
    • 直接使用 js/RegExp 构造正则表达式。

使用 clojure.string 函数而不是 String 方法。这也有助于在 clj 和 cljs 之间传输。

0
我们可能可以修补 RegExp 构造函数或篡改 String 原型链以自动实现 pattern→regexp 诱导,但这是一种暴力的解决方案。
纠正我错误的话,但在 Clojure (java) 代码中,使用 Pattern 和 Match 方法,或直接使用它们与 String 方法一起使用的情况非常罕见。大部分情况下它们被视为不透明对象,通过 re-* 和 clojure.string/** 使用。在 cljs 中以相同风格编写的代码将不受影响,并且我们可以将任何其他使用声明为平台特定的,并要求显式创建正则表达式(并且不要让 Matcher 或 Pattern 表现得像正则表达式)。这是在没有 too much 使用 RegExp.prototype.test、.exec 和 String.prototype.match、.replace、.search 和 .split 的情况下互联互通的首选方法。
...