CPP设计-string

这篇具有很好参考价值的文章主要介绍了CPP设计-string。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、IO

​ 是没有办法使用 C 风格的 IO 去输入和输出字符串的,也就是说,下面的程序是会发生段错误的。

#include <bits/stdc++.h>

using namespace std;

int main() 
{
	string s;
	scanf("%s", s);
	printf("%s", s);
	return 0;
}

​ 如果想要进行正确的 IO,需要利用 cin, cout ,如下所示

#include <bits/stdc++.h>

using namespace std;

int main() 
{
	string s1, s2, s3, s4;

	cin >> s1;
	// cin.getline(s2, 20);
	// cin.get(s3, 20);
	getline(cin, s4);

	cout << s1 << endl;
	// cout << s2 << endl;
	// cout << s3 << endl;
	cout << s4 << endl;
	return 0;
}

最常见的是下面,会发现读到空白符就会停止。

cin >> s1;

如果想要读取一整行,会发现这两个方法是没有办法通过编译的,这是因为这两个方法只能读取 C 风格字符串(即字符数组),无法读取

cin.getline(s2, 20);
cin.get(s3, 20);

所以想要读取整行,需要用这种方法

getline(cin, s4);

这种方法到结束符时,会将结束符一并读入指定的 string 中,再将结束符替换为空字符。

对于上面的程序,如果我输出

hello, world cnx!

会输出

hello, 
 world cnx!

当然如果想要使用 C 风格的 IO,可以这样操作

string s5;
scanf("%s", s5.c_str());
printf("%s\n", s5.c_str());

c_str() 会返回 string 内的字符数组头指针,就可以快乐 C 了。


二、初始化

总程序如下

#include <bits/stdc++.h>

using namespace std;

int main()
{
    string s1 = "hello";
	string s2("hello");
	cout << s1 << endl;
	cout << s2 << endl;

    string s3 = s1;
	string s4(s1);
    cout << s3 << endl;
	cout << s4 << endl;

    char *cs1 = "world";
    char cs2[] = "world";
	string s5(cs1);
	string s6(cs2);
	cout << s5 << endl;
	cout << s6 << endl;
    return 0;
}

2.1 字符串初始化

string s1 = "hello";
string s2("hello");
cout << s1 << endl;
cout << s2 << endl;

这两个调用的应该是一个方法,只不过是两种不同的调用形式,这两种都是可以的。这个我给他起名“类型转换构造器”,对于它的机理,应该是这样(我只实现了一个简易的 MyString ):

#include <bits/stdc++.h>

using namespace std;

class MyString 
{
public:
	int len;
	MyString(const char *s)
	{
		len = strlen(s);
		cout << "CAST CUSTRUCT" << endl;
	}
};

int main()
{
	MyString s1 = "hello, world";
	MyString s2("Hi, cnx");

	cout << s1.len << " " << s2.len << endl;
    return 0;
}

这个程序的输出是这样的

CAST CUSTRUCT
CAST CUSTRUCT
12 7

2.2 复制构造器

对于

string s3 = s1;
string s4(s1);
cout << s3 << endl;
cout << s4 << endl;

与 java 不同,string 的复制,并不是只复制了指向对象的指针,而是完全的进行了一次深拷贝,也就是产生了一个新的 string,上面的过程会发生在 a = b, a(b) 同时还有传参(其实也是一种初始化)的时候 ,就会调用这个构造器。

其基本原理大概是这样(相比于前一个,我增加了运算符重载的展示),下面尝试了“赋值,复制构造,传参,返回返回值”四种方法,调用的都是复制构造器。

#include <bits/stdc++.h>

using namespace std;

class MyString 
{
public:
	int len;

	MyString(const char *s): len(strlen(s))
	{
		cout << "CAST CONSTRUCT" << endl;
	}

	MyString(const MyString &s): len(s.len)
	{
		cout << "COPY CONSTRUCT" << endl;
	}

	friend ostream& operator<< (ostream &os, const MyString &s)
	{
		os << s.len;
		return os;
	}
};

MyString show(MyString s)
{
    cout << s.len << endl;
	return s;
}

int main()
{
	MyString s1 = "hello, world";
	MyString s2("Hi, cnx");
	cout << s1 << " " << s2 << endl;

	MyString s3 = s1;
	MyString s4(s1);
	show(s4);
	cout << s3 << " " << s4 << endl;

    return 0;
}

所以最终一共输出四次 copy

COPY CONSTRUCT
COPY CONSTRUCT
COPY CONSTRUCT
12
COPY CONSTRUCT

2.3 字符数组初始化

本质和用 string 进行初始化没有区别,可以正常工作就说明 string 内部实现了这种构造器。

char *cs1 = "world";
char cs2[] = "world";
string s5(cs1);
string s6(cs2);

三、比较运算

3.1 总论

​ 比较运算是一个非常非常必要了解的东西,这是因为大量的算法和数据结构都依赖与这些的定义,我把比较运算分为两类,一个是相等性判断,一种是比较判断。相等判断可以进行去重等操作,同时对于多次插入的值也有一定的影响,比较判断可以用于排序,还有构建有序的数据结构,比如说堆和红黑树。

3.2 相等性

代码如下

#include <bits/stdc++.h>

using namespace std;

int main()
{
	string s1 = "abc";
	string s2 = s1;
	string s3 = "ABC";
	printf("s1 address is 0x%x\n", &s1);
	printf("s2 address is 0x%x\n", &s2);
	printf("s3 address is 0x%x\n", &s3);

	if (s1 == s2)
	{
		printf("s1 == s2\n");
	}

	if (s1 != s2)
	{
		printf("s1 != s2\n");
	}

	if (s2 != s3)
	{
		printf("s2 != s3\n");
	}
	return 0;
}

其输出就是

s1 address is 0xecdf6110
s2 address is 0xecdf6130
s3 address is 0xecdf6150
s1 == s2
s2 != s3

可以看到,这些字符串是完全不同的独立的字符串,在 java 中,只要地址不用,那么就无法判断等于,而在 cpp 中,== 是逻辑上的,而不是地址比价上的。

在没有定义一个自定义结构体的 == 时,直接让 s1 == s2 会报一个 not match 的错误(从这里可以看出,在 CPP 中并没有“一切皆对象”的效果,现在看来,这里意味着,对一个普通的类,并没有像 java 中的 Object 一样的 equal 一样的“保底”方法 ),所以如果需要有相等性的比较的时候,我们需要定义 == 运算符。如下所示

#include <bits/stdc++.h>

using namespace std;

class MyString
{
public:
    int len;

    MyString(string s)
    {
        len = s.length();
    }

	// bool operator== (const MyString &other)
	// {
	// 	cout << "EQUEL OPERATOR" << endl;
	// 	return len == other.len;
	// }
};

bool operator== (const MyString &a, const MyString &b)
{
	cout << "EQUEL OPERATOR" << endl;
	return a.len == b.len;
}

int main()
{
    MyString s1("hello");
    MyString s2("world");
    MyString s3("1");

    if (s1 == s2)
    {
        printf("s1 == s2\n");
    }
    else
    {
        printf("s1 != s2\n");
    }

    if (s1 == s3)
    {
        printf("s1 == s3\n");
    }
    else
    {
		printf("s1 != s3\n");
    }
    return 0;
}

​ 定义的两种方式都是可以的,类内的定义会更加优先。

3.3 比较性

​ 就是按照字典序进行比较,十分好理解,如果从这个角度看,其实 string 已经像一个基本的类型了。

#include <bits/stdc++.h>

using namespace std;

int main()
{
	string s1 = "123456";
	if (s1 < "234")
	{
		printf("\"123456\" < \"234\"\n");
	}
	else
	{
		printf("\"123456\" >= \"234\"\n");
	}
    return 0;
}

​ 可以看到不但可以 stringstring 比,对于 string字符串 也是可以比的,cpp 中的字符串本质是 const char[]

​ 对于比较性,如果考虑自己实现,一共有三种定义比较性的方法

#include <bits/stdc++.h>

using namespace std;

class MyString
{
public:
    int len;

    MyString(string s)
    {
        len = s.length();
    }

    MyString()
    {
        len = 0;
    }

    bool operator==(const MyString &other)
    {
        cout << "EQUEL OPERATOR1" << endl;
        return len == other.len;
    }

    bool operator<(const MyString &other)
    {
        cout << "COMPARE OPERATOR1" << endl;
        return len < other.len;
    }
};

bool operator==(const MyString &a, const MyString &b)
{
    cout << "EQUEL OPERATOR2" << endl;
    return a.len == b.len;
}

bool operator>(const MyString &a, const MyString &b)
{
    cout << "COMPARE OPERATOR2" << endl;
    return a.len > b.len;
}

struct COMPARE
{
    bool operator()(const MyString &a, const MyString &b)
    {
        cout << "COMPARE OPERATOR3" << endl;
        return a.len < b.len;
    }
};

int main()
{
    MyString s1("hello");
    MyString s2("world");
    MyString s3("1");

    if (s1 < s2)
    {
        printf("s1 < s2\n");
    }
    else
    {
        printf("s1 >= s2\n");
    }

    MyString ss[3];
    ss[0] = s2;
    ss[1] = s1;
    ss[2] = s3;
    sort(ss, ss + 3, COMPARE());
    for (int i = 0; i < 3; i++)
    {
        cout << ss[i].len << endl;
    }
    return 0;
}

其输出如下

COMPARE OPERATOR1
s1 >= s2
COMPARE OPERATOR3
COMPARE OPERATOR3
COMPARE OPERATOR3
1
5
5

对于第一种

bool operator<(const MyString &other)
{
    cout << "COMPARE OPERATOR1" << endl;
    return len < other.len;
}

注意 CPP 没有那么智能,即使是 s1 >= s2 也是不行的,必须是 s1 < s2

第二种

bool operator>(const MyString &a, const MyString &b)
{
    cout << "COMPARE OPERATOR2" << endl;
    return a.len > b.len;
}

当然其实也可以不重载运算符,而是直接写个函数。

第三种

struct COMPARE
{
    bool operator()(const MyString &a, const MyString &b)
    {
        cout << "COMPARE OPERATOR3" << endl;
        return a.len < b.len;
    }
};

所谓的函数对象就是“像函数一样的对象”,也就是完成了重载 () 的对象。其调用的时候,调用的是类

sort(ss, ss + 3, COMPARE());

四、字符串格式化

探讨这个东西是因为我突然发现,用 cpp 实现一个 to_string 十分的困难,究其原因,是 cpp 中没有一个和 python 中 format 一样,或者 C 中 sprintf,完全无法格式化字符串。所以必须要用 streamstream 进行愚蠢的字符串格式化。

同时会发现 to_string 是一个很困难的事情其实,所以没有 gc 的语言感觉好难啊。

char *to_cstring() 
{
    // memory leak without delete[]
    char *s = new char[30];
    sprintf(s, "MyString len is %d", len);
    return s;
}

string to_string()
{
    stringstream format;
    format << "MyString len is " << len;
    return format.str();
}

五、遍历

​ 可以说,可以用 [] 进行访问。可以用迭代器访问,可以用加强 for 循环访问

#include <bits/stdc++.h>

using namespace std;

int main()
{
	string s = "abcde";

	for (int i = 0; i < s.length(); i++)
	{
		cout << s[i];
	}
	cout << endl;

	for (string::iterator iter = s.begin(); iter < s.end(); iter++)
	{
		cout << *iter;
	}
	cout << endl;

	for (auto iter = s.rbegin(); iter < s.rend(); iter++)
	{
		cout << *iter;
	}
	cout << endl;

	for (char &c : s)
	{
		c += 1;
	}

	for (auto c : s)
	{
		cout << c;
	}
	cout << endl;

	return 0;
}

这两种被叫做加强 for 循环

for (char &c : s)
{
    c += 1;
}

for (auto c : s)
{
    cout << c;
}
cout << endl;

可以看到,如果是加一个 & 引用,是可以修改它的内容的,否则是不能修改内容的(可以通过编译,但是修改不起作用)。


六、其他功能

6.1 删除

其实删除在字符串中应用并不多,但是使用迭代器删除的效果在 java 中有体现,在 cpp 中更加恶心,java 只是没法用加强 for 循环,只要用了迭代器啥事没有,但是在 cpp 中,即使使用了迭代器,删除会变得更加恶心,比如说下面这样的代码

#include <bits/stdc++.h>

using namespace std;

int main()
{
    string s = "abcde";

	for (auto iter = s.begin(); iter < s.end(); iter++)
	{
		s.erase(iter);
		cout << s << endl;
	}
    return 0;
}

这是输出

bcde
bde
bd

这是因为当一个东西被删除了之后,它的迭代器会指向下一个元素,而 for 会导致让原本就指向下一个元素的迭代器,指向下下个元素,这就导致了无法连续删除的现象。

6.2 长度

s.length();
s.size(); // 似乎这种更加常用

没有区别,都是可以使用的。同时他不会统计结尾的空白符。

6.3 查找子串

如果找得到的话,就会返回子串开始的位置的下标(这是独特的,因为在其他的 stl 容器中,会返回迭代器,而不是一个整型量),如果没有找到,则返回 -1 。如下所示

查找一般是两个方法,一种是从 index = 0 开始查找,这样只需要传入待查找子串一个参数,而另一种需要传入两个参数,可以指定开始查找的位置。下面的例子展示了这两种用法,用于找出 str 中的所有 substr

for (int pos = str.find(substr); pos != -1; pos = str.find(substr, pos + 1))
{
    cout << pos << endl;
}

6.4 替换

​ 替换函数 replace 需要给出三个参数,分别指定开始位置 pos,替换的长度 len 和替换串 str,这三个变量是缺一不可的,这是因为我们需要 pos, len 去描述被替换的子串的大小,而 str 是替换子串的内容。

​ 至于为啥不把 pos, len 合并成一个 string,这就不知道了,如下所示文章来源地址https://www.toymoban.com/news/detail-411603.html

string s1 = "abcde";
string s2 = "xyz";
// 将 [0, 1) 的内容 ("x") 替换成 "xyz"
s1.replace(0, 1, s2);
// xyzbcde
cout << s1 << endl;

到了这里,关于CPP设计-string的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • mysql 批量数据插入很慢(kettle 输入输出组件) 性能优化办法

    背景 最近在做数仓重构项目,遇到一些性能瓶颈,这里记录一下解决办法。 随着业务数据每天都在增加,几年前开发的etl任务开始跑不动了。大表一般是通过增量的方式插入,但是修复bug 或者每月/季度跑一次的情况 需要跑全量,原来的etl任务可能需要跑几个小时,甚至出

    2024年01月17日
    浏览(34)
  • 戴尔笔记本开机输入密码后黑屏只有鼠标,没有桌面的解决参考办法

    按 win + R 输入 cmd 回车(看不到的盲输,再按 Alt + Tab 验证); 再输入 explorer.exe 回车,启动资源管理器; 大部分电脑开机按 F8 进入安全模式,再用杀毒软件杀毒或卸载有问题的软件; 按 F8 没有反应的,需要开机后操作: 1.按 win + R 输入 msconfig 回车(看不到的盲输,再按

    2024年02月13日
    浏览(192)
  • 面向对象程序设计 之 文件输入输出流

    石 家 庄 铁 道 大 学 实 验 报 告 课程名称 面向对象程序设计 班级   姓名   学号   实验日期 2023.5.16 评分 100   实验项目名称:输入输出流 一、实验目的 掌握文本文件和二进制文件的基本访问方法; 了解一般I/O流和文件流的关系;了解文件与文件流的关系; 了解文件系统

    2024年02月05日
    浏览(36)
  • FPGA设计时序约束二、输入延时与输出延时

    目录 一、背景 二、set_input_delay 2.1 set_input_delay含义 2.2 set_input_delay参数说明 2.3 使用样例 三、set_output_delay 3.1 set_output_delay含义 3.2 set_output_delay参数说明 3.3 使用样例 四、样例工程 4.1 工程代码 4.2 时序报告 五、参考资料     为了在设计中准确的模拟信号从FPGA传输到外部或

    2024年02月07日
    浏览(31)
  • 【C++ 程序设计】第 7 章:输入/输出流

    目录 一、流类简介 二、标准流对象  三、控制I/O格式  (1)流操纵符  (2)标志字 四、调用cout的成员函数【示例一】 五、调用 cin 的成员函数  (1)get() 函数  (2)getline() 函数 (3)eof() 函数  (4)ignore() 函数  (5)peek() 函数  C++ 中凡是数据从一个地方传输到另一个

    2024年02月11日
    浏览(28)
  • Java程序设计:选实验6 输入输出应用

    (1) 编写一个程序,如果文件Exercisel_01.txt 不存在,就创建一个名为Exercisel_01.txt 的文件。向这个文件追加新数据。使用文本I/O将20个随机生成的整数写入这个文件。文件中的整数用空格分隔。 (2) 编写一个程序,如果文件Exercisel_02.dat 不存在,就创建一个名为Exercisel_02.dat 的文件

    2024年01月19日
    浏览(33)
  • 7.【CPP】String类

    我们知道计算机存储英文字母,标点,数字用的是ascall码,128种用一个字节表示绰绰有余。而汉字远远不止128种,因此 汉字需要两个字节表示 。 1.gbk编码中汉字占两个字节。 2.utf-8中,一个汉字占三个字节。 UTF规定:如果一个符号只占一个字节,那么这个8位字节的第一位就

    2024年01月22日
    浏览(17)
  • Cpp学习——string(2)

      目录 ​编辑 容器string中的一些函数 1.capacity() 2.reserve()  3.resize() 4.push_back()与append() 5.find系列函数 1.capacity() capacity是string当中表示容量大小的函数。但是string开空间时是如何开的呢?现在就来看一下。先写这样一段程序: 结果: 在这里可以看到除了第一次扩容以后,其它

    2024年02月14日
    浏览(28)
  • Cpp学习——string模拟实现

      目录 一,string的成员变量 二,string的各项功能函数 1.构造函数 2.析构函数 3.扩容函数 4.插入与删除数据的函数 5.+=运算符重载 6.打印显示函数 7,拷贝构造 8.find函数     在模拟实现string之前,首先就要先知道string是个啥子。其实string可以简单的理解为一个管理字符的顺序

    2024年02月12日
    浏览(25)
  • C程序设计实验报告2——数据类型、运算符和简单的输入输出

    1. 实验目的 (1)掌握C语言数据类型,了解字符型数据和整型数据的内在关系。 (2)掌握对各种数值型数据的正确输入方法。 (3)学会使用C的有关算术运算符,以及包含这些运算符的表达式,特别是自加(十十)和自减(—一)运算符的使用。 (4)学会编写和运行简单的应用程序

    2024年02月07日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包