检查空字符串是否在另一个字符串时,为什么返回True?

时间:2014-12-22 13:42:06

标签: python string python-internals

我有限的大脑无法理解为什么会这样:

>>> print '' in 'lolsome'
True

在PHP中,等效比较返回false:

var_dump(strpos('', 'lolsome'));

5 个答案:

答案 0 :(得分:55)

From the documentation

  

对于Unicode和字符串类型,当且仅当 x y 的子字符串时,x in y才为真。等效测试是y.find(x) != -1。注意, x y 不一定是同一类型;因此,u'ab' in 'abc'将返回True空字符串始终被视为任何其他字符串的子字符串,因此"" in "abc"将返回True

通过查看print来电,您正在使用2.x。

要深入了解,请查看字节码:

>>> def answer():
...   '' in 'lolsome'

>>> dis.dis(answer)
  2           0 LOAD_CONST               1 ('')
              3 LOAD_CONST               2 ('lolsome')
              6 COMPARE_OP               6 (in)
              9 POP_TOP
             10 LOAD_CONST               0 (None)
             13 RETURN_VALUE

COMPARE_OP是我们进行布尔操作的地方,查看in TARGET(COMPARE_OP) { w = POP(); v = TOP(); if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) { /* INLINE: cmp(int, int) */ register long a, b; register int res; a = PyInt_AS_LONG(v); b = PyInt_AS_LONG(w); switch (oparg) { case PyCmp_LT: res = a < b; break; case PyCmp_LE: res = a <= b; break; case PyCmp_EQ: res = a == b; break; case PyCmp_NE: res = a != b; break; case PyCmp_GT: res = a > b; break; case PyCmp_GE: res = a >= b; break; case PyCmp_IS: res = v == w; break; case PyCmp_IS_NOT: res = v != w; break; default: goto slow_compare; } x = res ? Py_True : Py_False; Py_INCREF(x); } else { slow_compare: x = cmp_outcome(oparg, v, w); } Py_DECREF(v); Py_DECREF(w); SET_TOP(x); if (x == NULL) break; PREDICT(POP_JUMP_IF_FALSE); PREDICT(POP_JUMP_IF_TRUE); DISPATCH(); } 显示比较发生的位置:

res = PySequence_Contains(w, v);

以及source code,我们可以轻松找到下一条线索:

{
    Py_ssize_t result;
    if (PyType_HasFeature(seq->ob_type, Py_TPFLAGS_HAVE_SEQUENCE_IN)) {
        PySequenceMethods *sqm = seq->ob_type->tp_as_sequence;
        if (sqm != NULL && sqm->sq_contains != NULL)
            return (*sqm->sq_contains)(seq, ob);
    }
    result = _PySequence_IterSearch(seq, ob, PY_ITERSEARCH_CONTAINS);
    return Py_SAFE_DOWNCAST(result, Py_ssize_t, int);
}

位于cmp_outcome is in the same file

objobjproc PySequenceMethods.sq_contains

并从源头获取空气,我们在abstract.c中找到了下一个功能:

PySequence_Contains()
     

此功能可由PySequence_Contains()使用,并具有相同的签名。此插槽可以留给 NULL ,在这种情况下,int PySequence_Contains(PyObject *o, PyObject *value) 只是遍历序列,直到找到匹配为止。

documentation

1
     

确定 o 是否包含。如果 o 中的项目等于,则返回0,否则返回-1。出错时,请返回value in o。这相当于Python表达式''

如果null不是'lolsome',则可以认为序列{{1}}包含它。

答案 1 :(得分:20)

引自PHP's strpos documentation

mixed strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )
     

needle字符串中查找第一次出现haystack 的数字位置。

所以你实际尝试的内容类似于下面的Python构造

>>> print 'lolsome' in ''
False

所以,你应该实际编写如下所示的在PHP中进行相应的比较

var_dump(strpos('lolsome', ''));

即使这样,它也会发出警告并返回false

  

PHP警告:strpos():第3行/home/thefourtheye/Desktop/Test.php中的空针

     

bool(false)

我挖得更深,发现the source code corresponding to the strpos function

    if (!Z_STRLEN_P(needle)) {
        php_error_docref(NULL, E_WARNING, "Empty needle");
        RETURN_FALSE;
    }

他们认为搜索的空字符串是一个有问题的案例。因此,他们发出警告并返回false。除此之外,我找不到任何文件,讨论为什么它被视为一个问题。

就Python而言,这种行为在Comparisons section

中得到了很好的定义
  

空字符串始终被视为任何其他字符串的子字符串,因此"" in "abc"将返回True

答案 2 :(得分:9)

基本上,从数学:

  

空集是每个集合的子集

这里的逻辑相同。您可以将''视为空集。因此,它是每个字符串集的子集,因为它们必须是相同的类型。

>>> a = ""
>>> b = "Python"
>>> a in b
True
>>> set(a).issubset(b)
True
>>> a = set() #empty set
>>> b = set([1,2,3])
>>> a.issubset(b)
True
>>> 

但要小心!子集和成员资格为different things

enter image description here

答案 3 :(得分:3)

  

空字符串是长度为零的唯一字符串   空字符串是连接操作的标识元素   空字符串位于字典顺序下的任何其他字符串之前,因为它是所有字符串中最短的   空字符串是一个合法的字符串,大多数字符串操作都应该在该字符串上工作   Wikipedia

 > strlen("");
=> 0
 > "a" . "" == "a";
=> true
 > "" . "a" == "a";
=> true   
 > "" < "\0";
=> true   

从上面看,PHP似乎将空字符串视为有效字符串。

> strstr("lolsome", "");
strstr(): Empty needle :1

但它似乎并不认为空字符串是完全合法的字符串。很可能PHP是唯一不允许在字符串中搜索子字符串为空字符串的语言。

这是一种防御机制吗?显然,程序员不必用if来保护针头。如果是这样,为什么其他语言允许这个测试通过!语言设计师必须回答

Python字符串由什么构成?

>>> ''.count('')
1

显然空字符串有一个空字符串。

>>> 'a'.count('')
2

一个元素字符串有两个空的srings。

>>> 'ab'.count('')
3

因此,Python字符串似乎是一个元素字符串的串联。字符串中的每个元素都夹在两个空字符串之间。

>>> "lolsome".split('')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: empty separator

但是这里的Python与空字符串的有效性相矛盾。 这是一个错误吗?
RubyJavaScript在这里通过测试。

 > "lolsome".split("")
=> ["l", "o", "l", "s", "o", "m", "e"]

我已经编译了Rosetta code中的几个语言示例,它的有趣的注意到它们都允许子字符串中的空字符串搜索并返回true。

AWK

awk 'BEGIN { print index("lolsome", "") != 0 }'

C

int main() {
    printf("%d\n", strstr("lolsome", "") != NULL);
    return 0;
}

C++

#include <iostream>
#include <string>

int main() {
    std::string s = "lolsome";
    std::cout << (s.find("") != -1) << "\n";
    return 0;
}

C#

using System;
class MainClass {
  public static void Main (string[] args) {
    string s = "lolsome";
    Console.WriteLine(s.IndexOf("", 0, s.Length) != -1);
  }
}

Clojure

(println (.indexOf "lolsome" ""))

Go

package main

import (
    "fmt"
    "strings"
)
func main() {
    fmt.Println(strings.Index("lolsome", "") != -1)
}

Groovy的

println 'lolsome'.indexOf('')

返回0,错误返回-1

Java

class Main {
  public static void main(String[] args) {
    System.out.println("lolsome".indexOf("") != -1);
  }
}

JavaScript

"lolsome".indexOf("") != -1

Lua

s = "lolsome"
print(s:find "" ~= nil)

Perl

print index("lolsome", "") != -1;

Python

"lolsome".find("") != -1

Ruby

"lolsome".index("") != nil

答案 4 :(得分:1)

假设你有两堆相似的物品,比如你最喜欢的诗人的最佳诗节,分别为5和2。更大的集合包含更小的集合吗?如何检查: 1)对于较小堆中的任何一节,你可能会发现它更大。 2)较小的桩不包含较大的桩。

因此我们可以使用此伪代码来检查:

for object in smaller:
    if object not in bigger:
       return 'we found object from smaller absent in bigger'
    else:
       go to next object
return 'all is ok - all objects from smaller are in bigger'

如果你还没有找到这样一个物体,你就会走到算法的尽头,认为较小的物体是较大物体的一部分。

现在想象一下小桩是0节。 应用上面相同的规则,我们执行0检查,并且也找不到较小的对象,而较大的对象不存在。

因此将空字符串视为任何其他字符串的子集是正确和方便的。即使是自己。这是在python中实现的。

    >>> '' in 'adfsf'
    True
    >>> '' in ''
    True