为什么 "abcd".StartsWith("") 返回 true?

89

标题就是整个问题。有人能给我一个为什么会发生这种情况的原因吗?

11个回答

167

是的 - 因为它确实以空字符串开头。实际上,在每对字符之间都逻辑上存在空字符串。

这样说吧:你能给出什么“以...开始”的定义,使得这种情况不成立吗?以下是一个简单的“以...开始”的定义,它并不排除这种情况:

“如果x的前y.Length个字符与y的相同,则x以y开头。”

另一种(等价的)定义:

“如果x.Substring(0, y.Length).Equals(y),则x以y开头。”


8
是的,你确实可以明确排除这种情况。不过这个定义相当不优雅,对吧? - Jon Skeet
7
没错,这也要求它们不为空...但我认为这两个条件增加了准确性,但从简单定义的本质上说,削弱了一些。 - Jon Skeet
8
每对字符之间都有无限次空字符串出现。 - Lotus Notes
3
是的,这就像在数字前面加一个零——它不会改变其值,但它仍然存在。 - pop850
3
@Deepak:什么样的例子,确切地说? - Jon Skeet
显示剩余7条评论

48

我将尝试详细说明Jon Skeet所说的内容。

假设x、y和z是字符串,+运算符实际上是连接操作,那么:

如果我们可以把z分解为z = x + y,那就意味着z以x开头。 因为每个字符串z都可以被分解为z = "" + z,所以每个字符串都以""开头。

所以,因为("" + "abcd")== "abcd",所以"abcd"以""开头。


19

我将从一个更易理解的相关事实开始。

空集是每个集合的子集。

为什么?子集定义指出,如果A的每个元素都是B的元素,则AB的子集。反之,如果A存在一个不是B的元素,则A不是B的子集。

现在固定一个集合B。我将证明空集是B的子集。我将通过展示不是空集不是B的子集来证明这一点。如果空集不是B的子集,则我可以找到一个空集的元素不在B中。但是,空集没有任何元素,因此我无法找到不在B中的元素。因此,空集不是不是B的子集。因此,空集必须是B的子集。

任何字符串都以空字符串开头。

首先,我们必须就“以...开始”这一定义达成共识。假设有字符串st,我们说st开头,如果s.Length >= t.Lengtht的前t.Length个字符与s的相同。也就是说,s.Length >= t.Length,对于每个满足0 <= index < t.LengthInt32 index,都有s[index] == t[index]。反之,如果语句 s.Length < t.Length或者s.Length >= t.Length且存在一个Int32 index满足0 <= index < t.Lengths[index] != t[index] 成立,我们将说s不以t开头。换言之,st短,或者在t中有一个字符与s中相应位置上的字符不同。
现在修复一个字符串s。我会确认s以空字符串开头。我将通过展示不是s不以空字符串开头来实现这一点。如果s不以空字符串开头,则s.Length < String.Empty.Lengths.Length >= String.Empty.Length,并且存在一个Int32 index,使得0 <= index < String.Empty.Length。但是s.Length >= 0,而String.Empty.Length等于零,因此s.Length < String.Empty.Length为真是不可能的。同样,由于“String.Empty.Length”等于零,不存在满足0 <= index < String.Empty.LengthInt32 index。因此

s.Length < String.Empty.Lengths.Length >= String.Empty.Length,并且存在一个Int32 index,使得0 <= index < String.Empty.Length

是假的。因此,不是s不以空字符串开头。因此,s必须以空字符串开头。

以下是作为string扩展编码的starts with的实现。

public static bool DoStartsWith(this string s, string t) {
    if (s.Length >= t.Length) {
        for (int index = 0; index < t.Length; index++) {
            if (s[index] != t[index]) {
                return false;
            }
        }
        return true;
    }
    return false;
}

上述两个加粗的事实是真空真实陈述的例子。它们之所以成立,是因为定义它们的语句(子集以...开始)是针对空集的全称量化。空集中没有元素,因此不能有任何不在某个其他固定集合中的空集元素。空字符串中没有字符,因此不能有一个字符作为空字符串中某个位置与某个其他固定字符串中同一位置的字符不匹配。

17

该方法将value参数与此字符串开头与value长度相同的子字符串进行比较,并返回指示它们是否相等的值。为了相等,value必须是空字符串(Empty)、对此实例的引用或匹配此实例开头的字符串。

.NET String.StartsWith

如果参数表示的字符序列是该字符串所表示的字符序列的前缀,则返回true;否则返回false。还请注意,如果参数是空字符串或根据equals(Object)方法确定与此String对象相等,则将返回true。

Java String.startsWith


13

假设 "abcd".StartsWith("") 返回 false。

那么以下表达式的值是 true 还是 false:

 ("abcd".Substring(0,0) == "")

事实证明它的值为真,因此该字符串确实以空字符串开头 ;-),或者换句话说,从位置0开始并且长度为0的“abcd”子字符串等于空字符串“”。在我看来相当合乎逻辑。


“abcd”.Substring(0, 0) 返回一个空字符串,并不意味着“abcd”实际上以空字符串开头。结果同样可以被声明为“未定义”,因为null也是一个同样适当的返回值。 - Tom Lint
@TomLint 不是的。通常你会将条件组合起来,例如 x.FirstName.StartsWith(userEnteredFirstName) && x.LastName.StartsWith(userEnteredLastName) .... 这样即使其中一个输入值为空字符串,条件也能够生效。 - Pop Catalin

7
在C#中,规范中这样告诉它如何反应:
要相等,值必须是一个空字符串(Empty),对于这个实例的引用,或者匹配该实例的开头。

5
为什么“abcd”.StartsWith(“”)返回true?
真正的答案是:
必须这样,否则你会遇到这种情况
    "".startsWith("") == false 
    "".equals("") == true

    but yet

    "a".startsWith("a") == true
    "a".equals("a") == true

然后我们会再次经历Y2K问题,因为所有依赖于以自身开头的相等字符串的银行软件将混淆我们的账户,突然间比尔·盖茨将拥有我的财富,而我却有他的,该死的!命运对我不是那么仁慈。


我不同意。"".startsWith("")应该等于true,因为这两个字符串是完全相同的,而不是因为某种奇怪的业务逻辑。 - Tom Lint
@TomLint,你实际上是在赞同答案中的断言... - neonblitzer

5
两个字符串的前N个字符是相同的,其中N为第二个字符串的长度,即零。

4
仅供参考,String.StartsWith() 内部调用了方法 System.Globalization.CultureInfo.IsPrefix(),该方法明确进行以下检查:
if (prefix.Length == 0)
{
    return true;
}

1

因为一个字符串以“nothing”开头。


1
这是前提是字符串为空时才成立!这种过度简化不能泛化。我建议我们遵循更多集合论导向的讨论。 - Pita.O

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