在C++中,一个空字符串是否包含一个空字符串?

6

我在我的问题的评论中有了一个有趣的争论。我的对手声称,语句""不包含""是错误的。

我的理解是,如果""包含另一个"",那么那个也会包含"",以此类推。

谁错了?

P.S.

我在谈论一个std::string

P.S. P.S

我不是在谈论子字符串,但是即使我在我的问题中添加“作为子字符串”,它仍然没有意义。一个空的子字符串是无意义的。如果允许在字符串中包含空子字符串,那么这意味着你有一个无穷多的空子字符串。这有什么意义呢?

编辑:

我是唯一一个认为std::string::find函数存在问题的人吗?

C++参考文献明确说明

返回值:第一个匹配项的第一个字符的位置。

好吧,假设有道理,并运行以下代码:

string empty1 = "";
string empty2 = "";

int postition = empty1.find(empty2);

cout << "found \"\" at index " << position << endl;

输出结果为:在索引 0 处找到 "" 无意义的部分:一个长度为 0 的字符串怎么可能有索引 0?这是无意义的。
要使字符串具有第 0 个位置,它必须至少包含 1 个字符。
而在这种情况下,C++ 给出了一个异常,证明了我的观点。
cout << empty2.at( empty1.find(empty2) ) << endl;

如果它真的包含空字符串,那么打印它出来就没有问题了。

10
我不确定我理解了。一个空杯子是否包含一个空的杯子? - jrok
我真的不理解你在这里问什么。 - Borgleader
@jrok 这是一个不同的问题(而且更容易解决)。 - StephenTG
你忘记在问题中添加“作为子字符串”,导致出现类型错误。没有任何一个字符串包含另一个字符串“作为元素”(字符串的元素是字符,而不是字符串)。 - n. m.
1
一个空的子字符串就像0加法一样毫无意义。毕竟,如果我们允许使用0进行加法运算,那么我们将有无限个加0的结果。 - molbdnilo
显示剩余13条评论
7个回答

16

这要取决于你所说的“包含”的意思。

空字符串是空字符串的子字符串,因此在这种意义下被包含。

另一方面,如果你将一个字符串视为字符的集合,则空字符串不能包含空字符串,因为它的元素是字符,而不是字符串。

与集合相关,集合

{2}

是集合的子集

A = {1, 2, 3}

但是 {2} 不是 A 的成员——所有 A 的成员都是数字,而不是集合。

同样地,{}{} 的子集,但是 {} 不是 {} 中的元素(它无法成为元素,因为它是空的)。

所以你们两个都是正确的。


8

C++ 赞同您的 "对手":

#include <iostream>
#include <string>
using namespace std;

int main()
{
    bool contains = string("").find(string("")) != string::npos;
    cout << "\"\" contains \"\": "
        << boolalpha << contains;
}

输出:"" 包含 "": true

演示


你认为你在C++实现中发现了一个错误吗? - n. m.
@n.m. 不,为什么?想想看,这是有道理的。 - jrok
开个玩笑,当然有意义(但似乎不是每个人都能理解)。 - n. m.
@n.m.:标准确切地定义了std::basic_string<>::find的语义,因此实现中没有错误。 - David Rodríguez - dribeas
有趣的是,我的“STL实现”(非常古老)给出了不同的结果,这是明确的[不是哲学问题](https://dev59.com/inXYa4cB1Zd3GeqP3C32#46093100);-) - Wolf

5
很简单。如果存在一个参数offset,使得A.substr(offset, B.size()) == B,则字符串A包含子串B。对于空字符串没有特殊情况需要考虑。
因此,让我们来看看。 std::string("") .substr(0,0)的结果是std::string("")。我们甚至可以检查你的“反例”。std::string("").substr(0,0).substr(0,0)也是明确定义的并且为空。一直到底了。

比较好的(并且“务实”的)解释。也许std::string("")应该被替换为std::string() - Wolf

0
今天我有同样的问题,因为我目前被一个糟糕的STL实现所束缚(追溯到C++98之前的时代),它与C++98和所有后续标准不同。
TEST_ASSERT(std::string().find(std::string()) == string::npos); // WRONG!!! (non-standard)

如果您尝试编写可移植代码,这将是特别糟糕的,因为很难证明没有任何功能依赖于该行为。不幸的是,在我的情况下,这确实是真的:它会对电话号码进行字符串处理,具体取决于用户线路规格而缩短输入。

在 Cppreference 上,我看到 std::basic_string::find 中有一个关于空字符串的显式描述,我认为正好符合所讨论的情况:

当且仅当 pos <= size() 时,在 pos 处找到空子字符串

所引用的 pos 定义了搜索开始的位置,默认为 0(开头)。

符合标准的 C++ 标准库 将通过以下测试:

TEST_ASSERT(std::string().find(std::string()) == 0);
TEST_ASSERT(std::string().substr(0, 0).empty());
TEST_ASSERT(std::string().substr().empty());

这个“contain”的解释回答了问题,是肯定的。


0

当然,一个空字符串并不包含一个空字符串。如果它这样做了,那么它将一直是乌龟。

取一个声明为空的字符串文字String empty = "";,如果你想要一个表示空字符串文字的字符串文字,你需要String representsEMpty = """";,但是当然,你需要转义它,给你string actuallyRepresentsEmpty = "\"\"";

附言,我对此采取实用主义方法。把数学废话留在门外。

考虑到您的修改,可能您的“对手”意思是,“空”的std::string仍然具有内部存储字符的存储器,该存储器本身为空字符。我相信这将是一个实现细节,它可能只保留一定大小(比如10)的字符数组“以防万一”,因此从技术上讲,它不会是空的。

当然,还有一个诡计问题的答案,即“没有东西”可以无限次地适合任何东西,这是一种“除以零”的情况。


2
你没有理解他的意思。他在问一个空字符串是否是另一个空字符串的子字符串。当然是,因为空字符串是所有可能字符串的子字符串。 - Etienne de Martel
3
"string actuallyRepresentsEmpty = """"这句话并不代表一个空字符串,而是一个含有两个双引号的字符串!" - David Rodríguez - dribeas
@DavidRodríguez-dribeas您误解了。 它代表了您需要“编程”和空字符串。 我并不是说"\"\""是包含空字符串的空字符串,它是一个包含了代表空字符串的内容的字符串。 - thecoshman
-1 是指在回答编程问题时给出哲学性的答案。 - Wolf
好的,“哲学性”可能是一个错误,对此我很抱歉。但是你的回答真的有帮助吗?每天编程的一个重要点是特殊情况是否重要。您必须决定在将字符串传递给find()等函数之前是否检查其是否为空,并且由于可以从(空)字符串中获取空子字符串,因此找到这些(也许是“荒谬的”)模式是一致的行为。您认为有可能改进您的答案吗? - Wolf

0
第一件不清楚的事情是,你是在谈论std::string还是以null结尾的C字符串,第二件事是为什么这很重要?我会假设你是指std::string。
std::string上的要求决定了组件必须如何行事,而不是其内部表示必须是什么(尽管其中一些要求会影响内部表示)。只要满足组件的要求,它是否在内部持有某些内容就是一个你甚至可能无法测试的实现细节。
在特定情况下,对于空字符串,没有任何规定要求它必须持有任何内容。它可以只持有一个设置为0的大小成员和一个指针(用于动态分配的内存,如果/当非空),也设置为0。operator[]中的要求需要返回一个具有值0的字符的引用,但由于该字符不能被修改而导致未定义的行为,并且由于严格的别名规则允许从char类型的lvalue中读取,因此在空字符串的情况下,实现可以只返回到size成员中的一个字节的引用(所有都设置为0)。

一些std::string的实现使用小对象优化,在这些实现中,将为小字符串保留内存,包括一个字符串。虽然std::string在内部显然不会包含一个std::string,但它可能包含组成空字符串的字符序列(即终止空字符)。


组件的要求 - 这绝对是正确的。但是你手头有参考资料吗?这些关键情况在此处没有涵盖:string::find - C++ 参考?我必须使用(旧的)实现,对于 std::string().find(std::string()) 给出了 string::npos,这似乎不符合标准,或者不再符合标准了,参见 Coliru Viewer。提前致谢! - Wolf
1
@Wolf:我不会相信那个特定的文档来源,建议使用http://cppreference.com。标准C++03是我检查过的最旧版本,它将操作“xpos”的结果定义为字符串中的位置,使得“xpos + arg.size() <= size()”,并且在“xpos”之后对象中的值序列与“arg”中的值序列相同。值“0”符合要求:“0 + empty.size() <= empty.size()”(“0 <= 0”),并且由于“empty”中没有元素,因此第二个限制显然匹配。也就是说,“anything.find("") == 0”根据定义是成立的。 - David Rodríguez - dribeas
今天我偶然发现了一些奇怪的测试用例(标记为可疑),因此再次搜索了这个问题。我认为这不是关于实现细节,而是关于规范,所以(抱歉)我认为这会分散注意力。我添加了一个答案,引用了至少Cppreference(作为“标准”的代表)。 - Wolf

0

空字符串不包含任何内容 - 它是空的。:)


@DavidRodríguez-dribeas 这里没有什么哲学含义。你只需要在教科书中查找定义即可。 - n. m.
3
答案与编程有些不太相关,或者说并不是吗?std::string empty; std::string also_empty = empty.substr(0,0); 看起来表明你可以从一个空字符串中获取一个空字符串作为子串。另一个问题是 empty.find("") 的结果是 0 而不是 std::string::npos,这表示它在空字符串的开头找到了空字符串。 - David Rodríguez - dribeas
1
@n.m.:嗯,如果这不是哲学问题,那么这个答案在两个方面上都是错误的,它没有证据支持,并且违反了标准规定。 - David Rodríguez - dribeas
-1 这个答案表达了个人观点。我们最好不要基于观点来制定软件解决方案。 - Wolf

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