如何区分原始字符串(r'')和普通字符串('')?

12

我目前正在构建一个工具,需要将文件名与模式进行匹配。为了方便起见,我计划提供惰性匹配(类似于glob)和正则表达式匹配。例如,以下两个代码片段最终会产生相同的效果:

@mylib.rule('static/*.html')
def myfunc():
    pass

@mylib.rule(r'^static/([^/]+)\.html')
def myfunc():
    pass
据我所知,r'' 只对Python解析器有用,它实际上是在解析后创建一个标准的str实例(唯一的区别是它保留了\)。
有人知道如何区分这两者吗?
我不想为相同目的提供两个备选装饰器,更糟糕的是手动解析字符串以确定它是否为正则表达式。

4
另一个选项是通过传递编译后的正则表达式规则来实现,例如@mylib.rule(re.compile(r'^stat...')) - intuited
手动解析字符串以确定它是否为正则表达式?你会怎么做?你只是假设它是一个正则表达式,如果它不能编译,就假设它是“fnmatch”模式吗?我该如何覆盖这两个选择,以明确我的文件名中真的有一个 *?如果我的文件名中有 \\ 并且被迫使用 r" 字符串来提供“fnmatch”模式,我该怎么办? - S.Lott
@jleedev:太酷了!我猜这会更好一些,因为Ruby有正则表达式字面量。 - intuited
@S.Lott 使用启发式方法来区分这两个模式,并让用户明确地决定并不是非常困难。我没有仔细思考过,但在正常情况下,任何带有[]()字符的字符串几乎肯定是一个正则表达式。只要有文档,那就没问题了。但是正如我所说,我会使用两个装饰器以获得更大的清晰度。 - saalaa
我实际上想要的是在生成的“str”中有一些属性,可以帮助我区分它们。 - saalaa
显示剩余2条评论
3个回答

17

你无法区分它们。每个原始字符串字面值也可以被写成标准字符串字面值(可能需要更多的引用),反之亦然。除此之外,我一定会给这两个装饰器不同的名称。它们并不做相同的事情,它们做不同的事情。

示例(CPython):

>>> a = r'^static/([^/]+)\.html'; b = '^static/([^/]+)\.html'
>>> a is b
True

所以在这个特定的例子中,原始字符串字面值和标准字符串字面值甚至会导致相同的字符串对象。


4
因此,整个设计思路存在缺陷。用户必须明确声明是使用fnmatch、regexp还是两者都不是,不能进行任何假设或猜测。 - S.Lott
你的代码示例不相关,因为它没有突出显示 r'' 实际上在做什么。虽然在这个简单的情况下使用 'is' 比较字符串可能会有一些意外,但它似乎是有效的。尝试使用 a = r'^static/([^/]+)\.html'; b = '^static/([^/]+)\.html' 会更有见地。否则,我现在提供两个装饰器。 - saalaa
4
这个例子旨在展示原始字符串字面值和标准字符串字面值可能无法区分。在这里使用==而不是is并没有显示任何内容,因为例如"a" == u"a"会产生True,而在Python 2.x中完全可以区分str对象和unicode对象。为了给出一个有效的反例,我必须给出一个例子,其中标准字符串字面值和原始字符串字面值恰好最终成为相同的对象 - Sven Marnach
1
虽然我理解你的观点,但这背后还有更多内容,而不仅仅是检查对象标识。编译器会识别到两个字符串最终具有相同的内容,因此——作为一种优化而非“正常”、“文档化”或“预期”的行为(不同的实现可能不会以相同的方式工作)——最终使用“相同”的对象。关于这个主题,stackoverflow上有一个非常有趣的帖子。 - saalaa
2
@saalaa:我非常清楚这一点——这就是为什么我明确指出这是一个CPython示例的原因。我也知道,如果你只是将这两个赋值放在两行上,那么这个示例将会给出不同的结果。但是所有这些都不重要——展示一个标准字符串字面量和一个原始字符串字面量最终成为相同对象的单个示例证明,无论如何得到这个单个示例,你都无法在一般情况下区分它们。 - Sven Marnach

13

事实上,你无法确定一个字符串是否定义为原始字符串。个人而言,我会使用单独的装饰器来实现,但如果你不想使用,也可以使用命名参数(例如,对于通配符使用@rule(glob="*.txt"),对于正则表达式使用@rule(re=r".+\.txt"))。

或者,如果用户想要使用正则表达式,可以要求他们提供编译后的正则表达式对象,例如@rule(re.compile(r".+\.txt"))——这很容易检测到,因为它的类型不同。


4
术语“原始字符串”有些令人困惑,因为它听起来像是一种特殊类型的字符串——实际上,它只是一种特殊的语法形式,用于明确告诉编译器在字符串中不要对“\”字符进行解释。不幸的是,这个术语被创造出来是为了描述这种编译时的行为,但许多初学者认为它具有某些特殊的运行时特性。
我更喜欢称它们为“原始字符串字面量”,以强调它们使用不解释反斜杠语法来定义字符串字面量,这才是使它们“原始”的原因。原始字符串字面量和普通字符串字面量都创建字符串(或 str),生成的变量与其他任何字符串一样。使用不解释反斜杠的原始字符串文字所创建的字符串在任何方面都等同于使用转义反斜杠非原始方式定义的同一字符串。

《Python in a Nutshell》的最新版本使用了这个术语。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接