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

Mar*_*sis 29 python string python-internals

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

>>> print '' in 'lolsome'
True
Run Code Online (Sandbox Code Playgroud)

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

var_dump(strpos('', 'lolsome'));
Run Code Online (Sandbox Code Playgroud)

小智 55

从文档:

对于Unicode和字符串类型,x in y当且仅当xy的子字符串时才为真.等效测试是y.find(x) != -1.注意,xy不必是相同的类型; 因此,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
Run Code Online (Sandbox Code Playgroud)

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();
    }
Run Code Online (Sandbox Code Playgroud)

并且cmp_outcome在同一个文件中,很容易找到我们的下一个线索:

res = PySequence_Contains(w, v);
Run Code Online (Sandbox Code Playgroud)

abstract.c中:

{
    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);
}
Run Code Online (Sandbox Code Playgroud)

为了从源头获取空气,我们在文档中找到了下一个功能:

objobjproc PySequenceMethods.sq_contains
Run Code Online (Sandbox Code Playgroud)

此功能可以使用PySequence_Contains()并具有相同的签名.此插槽可以保留为NULL,在这种情况下,PySequence_Contains()只需遍历序列直到找到匹配项.

在同一文档中进一步说明:

int PySequence_Contains(PyObject *o, PyObject *value)
Run Code Online (Sandbox Code Playgroud)

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

如果''不是null,则'lolsome'可以认为序列包含它.


the*_*eye 20

引用PHP的strpos文档,

mixed strpos ( string $haystack , mixed $needle [, int $offset = 0 ] )
Run Code Online (Sandbox Code Playgroud)

查找的数字位置的第一次出现needlehaystack字符串.

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

>>> print 'lolsome' in ''
False
Run Code Online (Sandbox Code Playgroud)

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

var_dump(strpos('lolsome', ''));
Run Code Online (Sandbox Code Playgroud)

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

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

bool(false)

我深入挖掘并找到了与该strpos函数对应的源代码,

    if (!Z_STRLEN_P(needle)) {
        php_error_docref(NULL, E_WARNING, "Empty needle");
        RETURN_FALSE;
    }
Run Code Online (Sandbox Code Playgroud)

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

就Python而言,此行为在" 比较"部分中已明确定义,

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

  • 哈哈,这很棒,随着你进入PHP源代码,我进入Python源码,这个问题真的很好解决了. (5认同)

GLH*_*LHF 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
>>> 
Run Code Online (Sandbox Code Playgroud)

不过要小心!子集和成员资格是不同的东西.

在此输入图像描述

  • 空字符串是空集逻辑不适用于其他 Python 序列。`set([]).issubset(set([1,2,3]))` 是 `True`。但是,[1,2,3] 中的 [] 是 False。“元组”也是如此。空字符串是空集逻辑是错误的。 (2认同)

Niz*_*med 5

空字符串是长度为零的唯一字符串。
空字符串是串联操作的单位元素。
按照字典顺序,空字符串位于任何其他字符串之前,因为它是所有字符串中最短的。
空字符串是合法字符串,大多数字符串操作都可以在其上进行。
维基百科

 > strlen("");
=> 0
 > "a" . "" == "a";
=> true
 > "" . "a" == "a";
=> true   
 > "" < "\0";
=> true   
Run Code Online (Sandbox Code Playgroud)

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

> strstr("lolsome", "");
strstr(): Empty needle :1
Run Code Online (Sandbox Code Playgroud)

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

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

Python 字符串由什么组成?

>>> ''.count('')
1
Run Code Online (Sandbox Code Playgroud)

显然空串有一个空串。

>>> 'a'.count('')
2
Run Code Online (Sandbox Code Playgroud)

一个元素串有两个空串。

>>> 'ab'.count('')
3
Run Code Online (Sandbox Code Playgroud)

看来Python字符串是单元素字符串的串联。字符串中的每个元素都夹在两个空字符串之间。

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

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

 > "lolsome".split("")
=> ["l", "o", "l", "s", "o", "m", "e"]
Run Code Online (Sandbox Code Playgroud)

我从Rosetta 代码中编译了几个语言示例,有趣的是它们都允许在子字符串搜索中使用空字符串并返回 true。

AWK

awk 'BEGIN { print index("lolsome", "") != 0 }'
Run Code Online (Sandbox Code Playgroud)

C

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

C++

#include <iostream>
#include <string>

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

C#

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

克洛尤尔

(println (.indexOf "lolsome" ""))
Run Code Online (Sandbox Code Playgroud)

package main

import (
    "fmt"
    "strings"
)
func main() {
    fmt.Println(strings.Index("lolsome", "") != -1)
}
Run Code Online (Sandbox Code Playgroud)

格罗维

println 'lolsome'.indexOf('')
Run Code Online (Sandbox Code Playgroud)

返回 0,出错时返回 -1

爪哇

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

JavaScript

"lolsome".indexOf("") != -1
Run Code Online (Sandbox Code Playgroud)

卢阿

s = "lolsome"
print(s:find "" ~= nil)
Run Code Online (Sandbox Code Playgroud)

珀尔

print index("lolsome", "") != -1;
Run Code Online (Sandbox Code Playgroud)

Python

"lolsome".find("") != -1
Run Code Online (Sandbox Code Playgroud)

红宝石

"lolsome".index("") != nil
Run Code Online (Sandbox Code Playgroud)

  • C `strstr` 还会在 `haystack` 的开头找到空的 `needle`。考虑到如此多的 PHP 字符串函数都是按照 C 对应函数建模的(尽管没有 *strpos*),这有点令人惊讶。 (3认同)