首页 > 编程语言 >C++String类

C++String类

时间:2024-03-31 14:30:32浏览次数:28  
标签:string int C++ char str 字符串 size String

前言

大家好,我是jiantaoyab,本篇文章将给大家介绍String类的常用法,和模拟实现String类。

String介绍

在cplusplus中,对String有着下面的介绍。

The standard string class provides support for such objects with an interface similar to that of a standard container of bytes, but adding features specifically designed to operate with strings of single-byte characters.

The string class is an instantiation of the basic_string class template that uses char (i.e., bytes) as its character type, with its default char_traits and allocator types (see basic_string for more info on the template).

可以看到string类不是STL的容器,string类是basic_string类模板的实例化。

为什么basic_string要设计成类模板呢?

因为不同的语言有不同的编码,有gbk,unicode,ascii等,basic_string独立于所使用的编码来处理字节,如果用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或大小)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。

String类常用接口

目前的标 string 类有 106 个接口函数(包括构造和析构函数),如果考虑上默认参数,那么就一共有 134 不同的接口。其中有 5 个函数模板还会产生无穷多个各种各样的函数,所以我们只需要知道常用的接口就够了。

string类常见构造

string()构造空的string类对象,即空字符串
string(const char* s)用C-string来构造string类对象
string(size_t n, char c)string类对象中包含n个字符c
string(const string&s)拷贝构造函数

string类对象的容量操作

size返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串释放为空串,是返回true,否则返回false
clear清空有效字符,不改变底层空间的大小
reserve为字符串预留空间
resize将有效字符的个数该成n个,多出的空间用字符’\0’填充
int main()
{
    string s1("hello");
    s1.reserve(2); //hello 不变
	s1.reserve(1000);//申请1000个空间
	string s2("xxxxxxxxxxxxx");
	s2.resize(1000);//申请1000个空间+初始化为0
	s2.resize(5);//xxxxx只剩下5个,capacity不变
}

注意

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
    致,一般情况下基本都是用size()。

  2. clear()只是将string中有效字符清空,不改变底层空间大小。

  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。

注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。

  1. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

string类对象的访问及遍历操作

operator[]返回pos位置的字符,const string类对象调用
begin+ endbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
rbegin + rendbegin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器
范围for

string类对象的修改操作

push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+=在字符串后追加字符串str
c_str返回C格式字符串
find + npos从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置(包括pos)
rfind从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置(包括pos)
substr在str中从pos位置开始(包括pos),截取n个字符,然后将其返回
void test()
{
    //取文件的后缀名
    size_t pos = file.find('.');
    if(pos != npos)
    {
        string suffix = file.substr(pos);
    }  
    //取address
    string url("http://www.cplusplus.com/reference/string/string/find/");
    size_t start = url.find(':');
    string protocol = url.substr(0, start - 0);
    start += 3; //从www开始找
    size_t end = url.find('/', start);
    string address = url.substr(start, finish - start);
    
    //删除
    url.erase(0, npos);
}

string类非成员函数

operator+尽量少用,因为传值返回,导致深拷贝效率低
operator>>
operator<<
getline获取一行字符串
relational operators大小比较

String题目练习

字符串最后一个单词的长度

#include <iostream>
using namespace std;
int main() {
    string s;
    while(getline(cin, s))
    {
        size_t pos = s.rfind(' ');
        cout<<s.size() - pos -1 <<endl;
    }
}

验证回文串

class Solution {
public:
    bool isletter(char a)
    {
        if(a>='a'&&a<='z')
        return true;
        if(a>='A'&&a<='Z')
        return true;
        if(a>='0'&&a<='9')
        return true;
     
        return false;
    }
    bool isPalindrome(string s) {
        int left=0;
        int right=s.size()-1;
        while(left<right)
        {
            while(left<right&&!isletter(s[left]))
            {
                left++;
            }
             while(left<right&&!isletter(s[right]))
            {
                right--;
            }


            if(tolower(s[left])!=tolower(s[right]))
            {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
};

字符串相加

class Solution {
public:
    string addStrings(string num1, string num2) {
        string ret;
        int size1=num1.size()-1;
        int size2=num2.size()-1;    
        int carry=0;
        //nums1 和 nums2 其中一个还没走完
        while(size1 >= 0 || size2 >= 0)
        {
            int x = 0;
            if(size1 >= 0)
            {
                x = num1[size1]-'0';
                --size1;
            }
            int y = 0;
            if(size2 >= 0)
            {
                y = num2[size2]-'0';
                --size2;
            }
            int add_sum = x + y + carry;
            if(add_sum > 9)
            {
                carry = 1;
                add_sum -= 10;
            }
            else{
                carry=0;
            }
            ret += add_sum + '0';     
        }
        if(carry==1)
        {
            ret += '1';
        }
        reverse(ret.begin(), ret.end());
        return ret;    
    }
};

反转字符串 II

image-20240327213914377

class Solution {
public:
    void reverse(string& s,int x,int y)
    {
        int left=x;
        int right=y;
        while(left<right)
        {
            swap(s[left],s[right]);
            left++;
            right--;
        }
    }
    string reverseStr(string s, int k) {
        for(int i=0;i<s.size();i+=(2*k))
        {
            if(i+k<=s.size())
            {
                reverse(s,i,i+k-1);
                continue;
            }
            else
            {
                reverse(s,i,s.size()-1);
            }
        }
       return s;
    }
};

反转字符串中的单词 III

image-20240327214053527

class Solution {
public:
	string reverseWords(string s)
	{
        int Word_begin=0;
        int Word_end=0;
        int i=0;
        int lenth=s.length();
        while(i<lenth)
        {
            Word_begin=i;
            while(i<lenth&&s[i]!=' ')
            {
                 i++;//找空格位置
            }
            Word_end=i-1;
            while(Word_begin<Word_end)
            {
                swap(s[Word_begin],s[Word_end]);
                Word_begin++;
                Word_end--;
            }
            while(i<lenth&&s[i]==' ')
            {
                i++;
            }

        }
        return s;
    }

};

字符串相乘

image-20240327214237696

class Solution {
public:
    string multiply(string num1, string num2) {
        int size1=num1.length();
        int size2=num2.length();
        vector<int>v(size1 + size2);
        for(int i=0;i<size1;i++)
        {
            for(int j=0;j<size2;j++)
            {
                int x=num1[size1-i-1]-'0';
                int y=num2[size2-j-1]-'0';
                v[i+j] += x*y;
            }
        }
        for(int i=0,carry=0;i<v.size();i++)
        {
            v[i]+=carry;
            carry=v[i]/10;
            v[i]%=10;
        }


        string ret;
        for(int i=v.size()-1;i>=0;i--)
        {
            if(ret.empty()&&v[i]==0) continue;
            ret+=v[i]+'0';
        }

        return ret.empty()? "0" :ret;
    }
};

找出字符串中第一个只出现一次的字符

#include <iostream>
#include<string>
using namespace std;
 
int main() {
    string ch;
    cin >> ch;
    char c;
    int sum_ch[256] = { 0 };
    int size = ch.size();
    for (int i = 0; i<size; i++)
    {
        sum_ch[ch[i]]++;
    } 
    for (int i = 0; i<size; i++)
    {
        if (sum_ch[ch[i]] == 1)
        {
            cout << ch[i] << endl;
            return 0;
        }
    }
    cout<<-1;
    return 0;
}

String模拟实现

详细的代码大家可以看string模拟实现具体代码,下面我拆分成每个模块给大家介绍。

迭代器部分

typedef char* iterator;
typedef const char* const_iterator;
	const_iterator begin() const
	{
		return _str;
	}
	const_iterator end()const
	{
		return _str + _size;
	}
	iterator begin()
	{
		return _str;
	}
	iterator end()
	{
		return _str + _size;
	}

范围for的是调用迭代器实现的

string::iterator it = s1.begin();
	while (it != s1.end())
	{
		std::cout << *it << std::endl;
		it++;
	}

image-20240328214516411

构造、拷贝构造、赋值拷贝

string(const char* str = " ") //字符串默认给个'\0'
			:_size(strlen(str))
			, _capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)

		{
			string tmp(s._str);
			this->Swap(tmp);
		}

string& operator=(string& s)
		{
			if (this != &s)
			{
				string tmp(s);
				this->Swap(tmp);				
			}
			return *this;
		}

空间容量

void reserve(size_t n)
{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;

			}
}

void resize(size_t n, char ch = '\0')
{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				if (n > _capacity)	reserve(n);				
				memset(_str + _size, ch, n - _size);
				_size = n;
				_str[_size] = '\0';
			}
}

增删改查


string& insert(size_t pos, const char* str)
{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (len + _size > _capacity) reserve(len + _size);
			size_t end = _size + len;
			//整体向后移动len位
			while (end > pos + len) _str[end--] = _str[end - len];
			strncpy(_str + pos, str, len);
			_size += len;
			return *this;
}

string& erase(size_t pos = 0, size_t len = npos)
{
			assert(pos < _size);
			if (len == npos || pos + len > _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}
		
			return *this;
}

void append(const char* str)
{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}
			strcpy(_str + _size, str); //包括'\0' 也拷贝过来
			_size += len;
}

void push_bach(char ch)
{
			//空间不够增容
			if (_size == _capacity)
			{
				reserve(_size == 0 ? 4 : 2 * _capacity);
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';
}
size_t find(char ch)
{
			for (size_t i; i < _size; i++)
			{
				if (_str[i] == ch) return i;
				
			}
			return npos;
}

size_t find(const char* str, size_t pos = 0)
{
			char* p = strstr(pos + _str, str);
			if (p == nullptr) return npos;
			else return p - _str; //找到返回子串第一个字符的下标
}

char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}
string& operator+=(char ch)
		{
			push_bach(ch);
			return *this;
		}
string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

c语言几个常用的字符操作函数

可以看到上面实现的代码经常运用了c的字符操作函数,我在这里做一个总结。

strcpy

image-20240330115049708

  • 源字符串必须以 ‘\0’ 结束。
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变

模拟实现

char *my_strcpy(char* dest, const char*src)
{
	char *ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while ((*dest++ = *src++));
	return ret;
}

strncpy

image-20240330202805944

拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个 。

strcmp

比较的是字符串的内容,对应的ASCLL值

image-20240330202932874

  • 第一个字符串大于第二个字符串,则返回大于0的数字
  • 第一个字符串等于第二个字符串,则返回0
  • 第一个字符串小于第二个字符串,则返回小于0的数字

模拟实现

int my_strcmp (const char * src, const char * dst)
{
	int ret = 0 ;
	assert(src != NULL);
	assert(dest != NULL);
	while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
	++src, ++dst;
	if ( ret < 0 )
	ret = -1 ;
	else if ( ret > 0 )
	ret = 1 ;
	return( ret );
}

strstr

字符串查找,在str1里找str2,找到就返回第一次找到的位置,找不到返回NULL

image-20240330203313354

模拟实现

char* my_strstr(const char*str1, const char* str2)
{
	assert(str1 && str2);
	char* s1;
	char* s2;
	char* cp = str1;
	
	if (*str2 == '\0')
		return str1;
	//cp是来记录从这个字母开始比较的,如果这次比较失败就++,下个字母开始重新比较。
	while (*cp)
	{
		s1 = cp;
		s2 = str2;

		//while (*s1!='\0'  && *s2 != '\0' && *s1 == *s2)
		while (*s1 && *s2 && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return cp;
		}
		cp++;
	}

	//找不到
	return NULL;
}

strcat

字符串追加

image-20240330203632576

char *my_strcat(char *dest, const char *src) //字符串追加
{
	assert(dest != NULL);
	assert(src != NULL);
	char *ret = dest;
    //1.找尾
	while (*dest)
	{
		dest++;
	}
    //2.追加
	while ((*dest++ = *src++))
	{
		;
	}

	return ret;
}

自己给自己追加可以吗?

两个指针同时操作一个字符串,当*dest找到 ‘\0’ 之后,*src开始追加,当追加完一个字符后,*src往后走一位,*dest指向的 '\0’被覆盖,dest就必须往后走一位,找到下一个’\0’,找到之后src又开始追加,不断重复,导致程序崩溃。

C语言常用的内存函数

memcpy

image-20240330203910491

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
  • 这个函数在遇到 ‘\0’ 的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是未定义的

模拟实现

void *my_memcpy(void* dest, const void *str, size_t num)
{
	//一个字节一个字节拷贝
	void *ret = dest;
	assert(dest&&str);
	while (num--)
	{
		*(char*)dest = *(char*)str;
		dest = (char*)dest + 1;
		str = (char*)str + 1;
	}
	return ret;
}

memmove

image-20240330204109452

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠,就得使用memmove函数处理。

模拟实现

void*my_memove(void *dest, const void*str, size_t num)
{
	assert(dest&&str); 
	void *ret = dest;
	if (dest > str)
	{
		//后向前拷贝
		while (num--)
		{
			*((char*)dest+num) = *((char*)str+num);	
		}
	}
	else
	{
		//前向后拷贝
		while (num--)
		{
			*(char*)dest = *(char*)str;
			str = (char*)str+1;
			dest = (char*)dest + 1;
		}
	}
	return ret;
}

memset

从prt指向的字符串开始,插入num个 value

image-20240330204434127

memcmp

image-20240330204320840
比较从ptr1和ptr2指针开始的num个字节, 如果return <0 prt1<ptr2

标签:string,int,C++,char,str,字符串,size,String
From: https://blog.csdn.net/weixin_53425006/article/details/137182700

相关文章

  • C++原子操作与内存序 1
    问题#include<iostream>#include<thread>intmain(){ intsum=0; autof=[&sum](){ for(inti=0;i<10000;i++) sum+=1; }; std::threadt1(f); std::threadt2(f); t1.join(); t2.join(); std::cout<<"thesum......
  • 【c++】类和对象(六)深入了解隐式类型转换
    ......
  • C++: 虚函数,一些可能被忽视的细节
    C++:虚函数,一些可能被忽视的细节引言:关于C++虚函数,对某些细节的理解不深入,可能导致我们的程序无法按预期结果运行,或是表明我们对其基本原理理解不够透彻。本文详细解答以下几个问题:实现多态,忘记写virtual会怎么样?虚函数的默认参数可以重载吗?纯虚函数真的不能有实现吗?析构函数可......
  • C++ 中的 volatile 和 atomic
    C++中的volatile和atomic0.TL;DRstd::atomic用于多线程并发场景,有两个典型使用场景:原子操作:对atomic变量的操作(读/写/自增/自减)仿佛受互斥量保护。一般通过特殊的机器指令实现,比使用互斥量更高效限制编译器/硬件对赋值操作的重新排序volatile和多线程并发没有......
  • C++—vector的介绍及使用 && vector的模拟实现
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档目录文章目录前言一、vector的介绍及使用1.1vector的介绍1.2vector的使用1.2.1vector的定义1.2.2vectoriterator的使用1.2.3vector空间增长问题1.2.4 vector增删查改1.2.5 vector迭代器......
  • 矩阵匹配【华为OD机试JAVA&Python&C++&JS题解】
    一.题目-矩阵匹配从一个NM(N<=M)的矩阵中选出N个数,任意两个数字不能在同一行或同一列,求选出来的N个数中第K大的数字的最小值是多少。输入描述:输入矩阵要求:1<=K<=N<=M<=150输入格式:NMKNM矩阵输出描述:N*M的矩阵中可以选出M!/N!种组合数组,每个组合数组中第K大的数中的......
  • 文本统计分析【华为OD机试JAVA&Python&C++&JS题解】
    一.题目-文本统计分析有一个文件,包含以一定规则写作的文本,请统计文件中包含的文本数量规则如下文本以";“分隔,最后一条可以没有”;",但空文本不能算语句,比如"COMMANDA;;"只能算一条语句.注意,无字符/空白字符/制表符都算作"空"文本文本可以跨行,比如下面,......
  • C++ 计数器小全
    1.简介  这篇文章介绍了C++的计数器们,包括:cnt(累加计数器),sum(求和计数器),mul(累乘计数器),last(上一项记录器),flag(状态计数器)  下文中,我就会详细说这6种计数器2.详解(正文)        2-1.cnt详解        cnt的中文名是累加......
  • 【C++】内存管理
    在学习完类与对象后,在学习C++中,接下来需要学习的是内存管理,在本篇博客中将会讲解C++的内存管理。 一.C/C++内存分布在学习内存管理前,我们先来了解一下C/C++的内存分布。 一般来说,内存可以分为四个区域:栈区、堆区、数据段、代码段。栈区:用于存放非静态的局部变量,函......
  • C++与C语言
    C++之所以是C++,和面向过程的C语言相比,它加了一个类,还有一个是模板。 引入C语言这种面向过程的编译语言可以将待解的问题分解成若干个子问题,面向对象程序设计则是建立在结构化程序设计方法的基础上,完全避免了结构化程序设计方法中所存在的问题。程序=数据结构+算法(结构化......