String类

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析2

在这里插入图片描述


👉🏻了解string类

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>
    string;
  4. 不能操作多字节或者变长字符的序列。

string类官方文档

👉🏻string的迭代器

我们一般访问string的字符,一般都是使用运算符重载operator[]

#include <iostream>
using namespace std;
void Test1()
{
	string s1("hello");
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << " " ;
	}
		for (auto x : s1)
	{
		cout << x << " ";
	}
}
int main()
{
	Test1();
	return 0;
}

📜但是这里我们引入迭代器这个概念
C++中的迭代器是一种用于遍历容器元素的对象。它提供了一种统一的方式来访问容器中的元素,而不需要了解容器的内部实现细节。

迭代器可以分为输入迭代器、输出迭代器、前向迭代器、双向迭代器和随机访问迭代器等几种类型,每种类型具有不同的功能和限制。
在这里插入图片描述

使用迭代器可以通过以下方式访问容器的元素:

  • 通过解引用操作符*来获取当前迭代器指向的元素的值。
  • 通过递增操作符++将迭代器移动到容器中的下一个元素。
  • 通过递减操作符–将迭代器移动到容器中的上一个元素(仅适用于双向迭代器和随机访问迭代器)。

迭代器的使用可以帮助我们在不依赖具体容器类型的情况下,对容器中的元素进行遍历、访问和修改。这使得我们可以编写更加通用和灵活的代码。

需要注意的是,一些函数接口会要求必须是双向的迭代器,如果你传单向就不行,因为单向不支持–,而像随机型的迭代器大部分接口都能使用,因为它的功能最齐全。

📃迭代器使用语法

  1. 声明迭代器:使用容器的类型作为模板参数来声明迭代器。例如,vector< int>::iterator 是一个指向 vector< int> 容器中元素的迭代器。

  2. 初始化迭代器:可以使用容器的成员函数来初始化迭代器,或者使用迭代器的构造函数来初始化。例如,vector::iterator it = myVector.begin(); 将迭代器 it 初始化为指向 myVector 容器的第一个元素。

  3. 使用迭代器:可以使用迭代器来访问容器中的元素。例如,*it 可以获取迭代器 it 指向的元素的值,it++ 可以将迭代器 it 向后移动一个位置。

  4. 迭代器的操作:迭代器支持多种操作,例如 ++ 运算符用于将迭代器向后移动一个位置,-- 运算符用于将迭代器向前移动一个位置,== 运算符用于比较两个迭代器是否相等等。

迭代器的用法类似指针。

需要注意的是,不同类型的容器可能有不同类型的迭代器,因此在使用迭代器时需要根据具体的容器类型来选择相应的迭代器类型和语法

在这里我还要再介绍一下迭代器中的两个函数:

  • begin():

Returns an iterator pointing to the first character of the string.
None parameters
它的返回值是字符串的首个字符的位置。
没有参数

  • end():

Returns an iterator pointing to the past-the-end character of the string.
返回一个指向字符串结束位置的迭代器
It often used in combination with begin()

我们看个例子:

#include <iostream>
using namespace std;
void Test1()
{
	string s1("hello");
	string::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";

		++it;
	}
}
int main()
{
	Test1();
	return 0;
}

除了begin()和end(),还有反向迭代器,以及const 迭代器,可以参考string官方文档了解。
在这里插入图片描述

👉🏻string constructor

在这里插入图片描述
上述中,我们常用到的接口也就是有:

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

npos
在这里插入图片描述
由于是无符号整型,所以大小大概有40多亿。

👉🏻string operator=

在这里插入图片描述
The function of the operator= is to assign a new value to the string, replacing its current contents.

👉🏻string类对象的容量操作

c++提供这些函数:
在这里插入图片描述

  • resize:调整字符串大小
    grammer:
void resize (size_t n);
void resize (size_t n, char c);

Resizes the string to a length of n characters.

If n is smaller than the current string length, the current value is shortened to its first n character, removing the characters beyond the nth.

If n is greater than the current string length, the current content is extended by inserting at the end as many characters as needed to reach a size of n. If c is specified, the new elements are initialized as copies of c, otherwise, they are value-initialized characters (null characters).
如果c没有具体说明,就用\0来补

  • reserve:为字符串预留空间

Requests that the string capacity be adapted to a planned change in size to a length of up to n characters.

If n is greater than the current string capacity, the function causes the container to increase its capacity to n characters (or greater).
也就是说至少会开n characters,可能会多开一些。

In all other cases, it is taken as a non-binding request to shrink the string capacity: the container implementation is free to optimize otherwise and leave the string with a capacity greater than n.
缩小容量的请求是没有约束力的,所以这个缩小容量可能可以缩小,但也可能不会缩小,但编译器一般会优化不缩小。
在这里插入图片描述
VS2019编译器下,就没有缩小。

This function has no effect on the string length and cannot alter its content.

只需记住,reserve改变的只有capacity

  • clear:清空有效字符

Erases the contents of the string, which becomes an empty string (with a length of 0 characters).

  • empty:

Returns whether the string is empty (i.e. whether its length is 0).
Return value
if it is empty return 1,false otherwise

👉🏻string Element access

在这里插入图片描述

  • operator[]:
 char& operator[] (size_t pos);
 const char& operator[] (size_t pos) const;

Returns a reference to the character at position pos in the string.

  • at:
 char& at (size_t pos);
 const char& at (size_t pos) const;

Returns a reference to the character at position pos in the string.

The function automatically checks whether pos is the valid position of a character in the string (i.e., whether pos is less than the string length), throwing an out_of_range exception if it is not.
这个和operator[]差不多,只是会报错

  • back:

access the last character of the string.

This function shall not be called on empty strings.

  • front:

同理back,只是返回首位元素

👉🏻string modifiers

在这里插入图片描述

这里具体实现可查阅官方文档。
给一些重要的说明:

  • 追加接口首选operator+=
  • 一般赋值,用= 就行,若需求是赋值一小段,则用assign
  • 不介意经常使用insert/erase/replace,因为都涉及数据的挪动,耗时
  • string::swap好在是交换地址,减少调用拷贝构造次数,而库提供的Swap则会多次调用拷贝构造次数,但是如果是string类的swap,会优先使用string::swap,因为库提供的Swap是一个模板,编译器会优先调用现成的。

在这里插入图片描述

👉🏻String operations

![在这里插入图片描述](https://img-blog.csdnimg.cn/af8b17ac51334f038004f8268b00cbf8.png

  • c_str和data功能相似,都是获取char* string

  • find:
    在这里插入图片描述
    Return Value
    The position of the first character of the first match.
    If no matches were found, the function returns string::npos.

  • substr:

    Return Value
    A string object with a substring of this object

🍴find和substr合作分割网址协议、域名、资源

void Test3()
{
    string s = "https://www.baidu.com/default.html";
    size_t pos1 = s.find(':');
    string s1 = s.substr(0, pos1);
    size_t pos2 = s.find('/', pos1 + 3);
    string s2 = s.substr(pos1 + 3, pos2);
    string s3 = s.substr(pos2 + 1, string::npos);


    cout << s1 << endl;
    cout << s2 << endl;
    cout << s3 << endl;
}
  • find_first_of:
size_t find_first_of (const string& str, size_t pos = 0) const noexcept;

size_t find_first_of (const char* s, size_t pos = 0) const;

size_t find_first_of (const char* s, size_t pos, size_t n) const;

size_t find_first_of (char c, size_t pos = 0) const noexcept;

从字符串中找到任意符合序列中的单个字符就行了
find_last_of是从后往前找。

#include <iostream>       // std::cout
#include <string>         // std::string
#include <cstddef>        // std::size_t

int main ()
{
  std::string str ("Please, replace the vowels in this sentence by asterisks.");
  std::size_t found = str.find_first_of("aeiou");
  while (found!=std::string::npos)
  {
    str[found]='*';
    found=str.find_first_of("aeiou",found+1);
  }

  std::cout << str << '\n';

  return 0;
}

👉🏻 getline

getline 是 C++ 中的一个函数,用于从输入流中读取一行文本。它的语法如下:

std::getline(istream& input, string& line);

其中,input 是输入流对象,可以是 cin(标准输入流)或文件流等;line 是用于存储读取到的文本的字符串对象。

getline 函数会读取输入流中的一行文本,直到遇到换行符(‘\n’)为止,并将读取到的文本存储到 line 字符串中。读取过程中,换行符不会被存储在 line 中。

getline还可以更改定界符。

istream& getline (istream& is, string& str, char delim);

If the delimiter is found, it is extracted and discarded (i.e. it is not stored and the next input operation will begin after it).

👉🏻string总结

string str = “123456”
1.迭代器区间是左闭右开形式
(1):我想得到区间[0,3]的字符串,即结果为1234
则迭代器区间可以为[str.begin(),str.begin()+4);即直接加上这个字符串的长度

(2):最后n个字符不想访问了,右区间为表示为str.end()-n

2.insert函数
(1)单个位置插入
insert (size_t pos, const string& str);
插入pos位置,就是原本pos位置的字符往后移动让位

(2) 区间插入

比如我想将上述的23替换为aaa
可以先erase删除23erase(str.begin()+1,str.begin()+3),再insert(1,“aaa”);

3.erase函数
(1)删除单个字符
erase (size_t pos = 0, size_t len = npos);
给出位置,len = 1一定要给出

4.replace函数

比如我想将上述的23替换为aaa
str.replace(str.begin()+1,str.begin()+3,“aaa”)

5.c_str()

将string变为const char*

6.find和find_first_of
(1)find
size_t find (const string& str, size_t pos = 0)
size_t find (char c, size_t pos = 0)

找到全部符合的,从pos位置开始找,pos有缺省值0

找到了返回第一个符合的字符串的首个位置
没找到返回npos
(2):find_first_of
函数原型和find大致一样

唯一区别就是find_first_of只要符合单个符合的字符就行

7.string ->int

在 C++ 中,可以使用 std::stoi 函数将字符串转换为整型。示例如下:

#include <iostream>
#include <string>

int main() {
    std::string str = "12345";
    int num = std::stoi(str);
    
    std::cout << "String: " << str << std::endl;
    std::cout << "Integer: " << num << std::endl;

    return 0;
}

在上面的示例中,std::stoi 函数将字符串 "12345" 转换为整型数值,并存储在变量 num 中。转换后,可以将整型数值用于数学运算或其他操作。如果字符串无法转换为整型,std::invalid_argument 异常会被抛出。

8.整型变为string
在 C++ 中,可以使用 std::to_string 函数将整型转换为字符串。示例如下:

#include <iostream>
#include <string>

int main() {
    int num = 12345;
    std::string str = std::to_string(num);

    std::cout << "Integer: " << num << std::endl;
    std::cout << "String: " << str << std::endl;

    return 0;
}

上面的示例中,std::to_string 函数将整型数值 12345 转换为字符串,并将其存储在变量 str 中。转换后,可以将字符串用于输出或其他需要字符串类型的操作。

👉🏻字符编码标准

unicode编码

概述
Unicode 是一种字符编码标准,用于表示世界上几乎所有的字符和符号。它为每个字符分配了一个唯一的数字,称为码点(code point)。Unicode 码点的范围从 U+0000 到 U+10FFFF。

Unicode 使用不同的编码方案来表示这些码点,最常见的编码方案是 UTF-8、UTF-16 和 UTF-32。

  • UTF-8 是一种变长编码方案,使用 8 位(1 字节)到 32 位(4 字节)来表示不同的字符。它是 ASCII 字符集的扩展,兼容 ASCII 编码,可以表示世界上几乎所有的字符。
  • UTF-16 是一种定长或变长编码方案,使用 16 位(2 字节)来表示大部分常用字符,使用 32 位(4 字节)来表示辅助平面字符(如表情符号、特殊符号等)。
  • UTF-32 是一种定长编码方案,使用 32 位(4 字节)来表示所有的字符,无论是常用字符还是辅助平面字符。

在编程中,我们经常会遇到需要处理 Unicode 字符的情况。在大多数编程语言中,字符串类型通常支持 Unicode 编码,可以存储和操作 Unicode 字符。
在这里插入图片描述

我们编译器一般遵循的都是UTF-8编码格式
所以这是为什么如果二进制存储格式0开头的都是字母。
汉字都是1开头的,但一个汉字是2字节。

Gb2312

概述
GB2312是中国国家标准局于1980年发布的字符集标准,它是对汉字和拉丁字母的编码方式进行统一的规范。GB2312字符集包含了大约7000多个常用汉字和拉丁字母,其中包括了基本的汉字、标点符号、数字和拉丁字母。

GB2312使用双字节编码,每个字符占用两个字节。其中,第一个字节的范围是0xA1至0xF7,第二个字节的范围是0xA1至0xFE。通过这种编码方式,GB2312可以表示大部分常用的汉字和一些特殊字符。

GB2312字符集的编码方式在计算机中广泛应用于中文信息处理、文本编辑、网页编码等领域。然而,由于GB2312字符集的容量有限,无法涵盖所有的汉字和特殊字符,因此后来又出现了更为完善的字符集标准,如GBK和GB18030。

Gbk

概述
GBK(Guo Biao Kuai Jie)是中文字符集编码标准之一,也是中国国家标准。它是在GB2312的基础上进行扩展的,支持更多的汉字字符。

GBK编码使用16位(2个字节)来表示一个字符,可以表示包括简体中文、繁体中文和一些其他的汉字字符。GBK编码兼容ASCII编码,即前128个字符的编码与ASCII编码相同。

GBK编码的字符范围包括了GB2312编码的全部字符,以及一些其他的汉字字符。它的编码范围是0x8140到0xFEFE,其中高字节的范围是0x81到0xFE,低字节的范围是0x40到0xFE。

GBK编码在计算机中的应用非常广泛,特别是在中文操作系统、中文编程环境和中文网页等领域。在使用GBK编码时,需要注意编码的转换和处理,以确保正确地处理中文字符。

👉🏻vs和g++环境下string的内部情况

🌍VS
string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字
符串的存储空间:

  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间
union _Bxty
{ // storage for small buffer or pointer to larger one
 value_type _Buf[_BUF_SIZE];
 pointer _Ptr;
 char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。
其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
最后:还有一个指针做一些其他事情。
故总共占16+4+4+4=28个字节。
🌍g++
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指
针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
  • 指向堆空间的指针,用来存储字符串。’

👉🏻拷贝

浅拷贝

C++中的浅拷贝是指在对象拷贝时,只复制对象的成员变量的值,而不复制指向动态分配内存的指针。这意味着原始对象和拷贝对象将共享相同的指针,可能导致潜在的问题(比如析构同一块空间、一方修改会影响另一方)。

当进行浅拷贝时,如果原始对象和拷贝对象都指向同一块内存,当一个对象修改了这块内存的值时,另一个对象也会受到影响。这可能导致意外的行为和错误的结果。

深拷贝

C++中的深拷贝是指在拷贝对象时,会创建一个新的对象,并将原始对象的所有成员变量的值复制到新对象中。这包括动态分配的内存,即在堆上分配的内存。深拷贝确保新对象和原始对象是完全独立的,对一个对象的修改不会影响到另一个对象。

要实现深拷贝,通常需要自定义拷贝构造函数和拷贝赋值运算符重载函数。在这些函数中,需要手动分配新的内存,并将原始对象的值复制到新对象中。这样可以确保新对象拥有独立的内存空间,而不是简单地复制指针

写时拷贝

📃
C++中的写时拷贝(Copy-on-Write)是一种优化技术,用于在拷贝对象时延迟实际的拷贝操作。当一个对象被拷贝时,实际的拷贝操作并不会立即发生,而是在对象被修改时才会进行拷贝

具体来说,当一个对象被拷贝时,拷贝构造函数会创建一个指向原始对象的共享指针。只有当拷贝后的对象被修改时,才会进行实际的拷贝操作,将原始对象复制一份并进行修改,以确保每个对象都有自己的独立副本。

写时拷贝的好处是可以节省内存和提高性能,特别是在涉及大型对象的情况下。因为在拷贝操作之前,多个对象共享同一个内存块,只有在需要修改时才会进行实际的拷贝,避免了不必要的内存开销。
需要注意的是,写时拷贝只在特定的情况下才会发生,例如当一个对象被拷贝但没有被修改时,共享指针会继续指向原始对象,而不进行实际的拷贝操作。但一旦对象被修改,就会进行实际的拷贝操作,确保每个对象都有自己的独立副本


📃

写时拷贝就是一种拖延症,是在浅拷贝的基础之上增加了引用计数的方式来实现的。
在这里插入图片描述

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源
在这里插入图片描述

👉🏻正则表达式

C++ 标准库提供了对正则表达式的支持,你可以使用 <regex> 头文件中的类和函数来操作正则表达式。下面是一些常见的正则表达式操作:

  1. regex 类: std::regex 类表示一个正则表达式。你可以使用它来创建、匹配和搜索字符串。

  2. match 和 search 函数: std::regex_matchstd::regex_search 函数用于匹配和搜索字符串。前者要求整个字符串与正则表达式完全匹配,而后者可以在字符串的任何位置找到匹配。

  3. regex_iterator 类: std::regex_iterator 类允许你在字符串中迭代匹配正则表达式的子串。

  4. regex_replace 函数: std::regex_replace 函数允许你使用正则表达式来替换字符串中的匹配部分。

下面是一个简单的示例,演示如何使用 C++ 的正则表达式功能:

#include <iostream>
#include <regex>
#include <string>

int main() {
    std::string str = "Hello, 123456! How are you?";
    std::regex pattern("\\d+"); // 匹配数字

    // 使用 regex_search 查找匹配的子串
    std::smatch match;
    if (std::regex_search(str, match, pattern)) {
        std::cout << "Found match: " << match.str() << std::endl;
    } else {
        std::cout << "No match found." << std::endl;
    }

    // 使用 regex_iterator 迭代匹配的子串
    std::sregex_iterator iter(str.begin(), str.end(), pattern);
    std::sregex_iterator end;
    while (iter != end) {
        std::cout << "Found match: " << iter->str() << std::endl;
        ++iter;
    }

    // 使用 regex_replace 替换匹配的子串
    std::string replaced_str = std::regex_replace(str, pattern, "###");
    std::cout << "Replaced string: " << replaced_str << std::endl;

    return 0;
}

这个例子中,我们定义了一个正则表达式模式 \d+,它匹配一个或多个数字。然后我们使用 std::regex_search 函数在字符串中查找匹配的子串,并使用 std::regex_iterator 迭代所有匹配的子串。最后,我们使用 std::regex_replace 函数替换匹配的子串。

函数介绍

regex_matchregex_searchregex_replace 是 C++ 标准库中用于处理正则表达式的函数。它们的函数原型、参数意义和返回值如下:

  1. regex_match 函数:

    template <class BidirectionalIterator, class CharT, class Traits>
    bool regex_match(BidirectionalIterator first, BidirectionalIterator last,
                     std::basic_regex<CharT, Traits> const &e,
                     std::regex_constants::match_flag_type flags = std::regex_constants::match_default);
    
    • first, last:定义了要搜索的输入序列的范围,通常是迭代器对。
    • e:要匹配的正则表达式对象。
    • flags:匹配标志,控制匹配行为的选项,默认为 std::regex_constants::match_default
    • 返回值:如果整个序列匹配正则表达式,则返回 true,否则返回 false
  2. regex_search 函数:

    template <class BidirectionalIterator, class CharT, class Traits>
    bool regex_search(BidirectionalIterator first, BidirectionalIterator last,
                      std::match_results<BidirectionalIterator> &m,
                      std::basic_regex<CharT, Traits> const &e,
                      std::regex_constants::match_flag_type flags = std::regex_constants::match_default);
    
    • first, last:定义了要搜索的输入序列的范围,通常是迭代器对。
    • m:用于存储匹配结果的对象,类型为 std::match_results<BidirectionalIterator>
    • e:要匹配的正则表达式对象。
    • flags:匹配标志,控制匹配行为的选项,默认为 std::regex_constants::match_default
    • 返回值:如果找到序列中的任何部分与正则表达式匹配,则返回 true,否则返回 false
  3. regex_replace 函数:

    template <class OutputIterator, class BidirectionalIterator, class Traits, class CharT, class STraits, class SAlloc>
    OutputIterator regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last,
                                 std::basic_regex<CharT, Traits> const &e, std::basic_string<CharT, STraits, SAlloc> const &fmt,
                                 std::regex_constants::match_flag_type flags = std::regex_constants::match_default);
    
    • out:输出迭代器,用于指定替换结果的存储位置。
    • first, last:定义了要搜索的输入序列的范围,通常是迭代器对。
    • e:要匹配的正则表达式对象。
    • fmt:替换文本的格式。
    • flags:匹配标志,控制匹配行为的选项,默认为 std::regex_constants::match_default
    • 返回值:指向输出序列的尾部的迭代器。

正则表达式匹配规则

正则表达式是一种强大的模式匹配工具,用于在文本中查找和识别特定模式的字符串。以下是一些常见的正则表达式匹配规则:

  1. 字面量字符匹配

    • 字符:普通字符(例如字母、数字、标点符号)会与其自身匹配。
    • 转义字符:某些字符具有特殊含义,如 \.* 等,如果想要匹配它们本身,需要使用反斜杠 \ 进行转义。
  2. 字符类

    • 方括号 [ ]:用于匹配一个字符集合中的任何一个字符。例如,[abc] 可以匹配 abc
    • 范围表示:使用连字符 - 来表示字符的范围。例如,[0-9] 表示匹配任何一个数字。
  3. 重复匹配

    • *:匹配前一个元素零次或多次。
    • +:匹配前一个元素一次或多次。
    • ?:匹配前一个元素零次或一次。
    • {n}:匹配前一个元素恰好 n 次。
    • {n,}:匹配前一个元素至少 n 次。
    • {n,m}:匹配前一个元素至少 n 次,但不超过 m 次。
  4. 位置匹配

    • ^:匹配输入字符串的开始位置。
    • $:匹配输入字符串的结束位置。
  5. 通用字符匹配

    • .:匹配除换行符 \n 外的任何单个字符。
  6. 逻辑操作符

    • |:表示逻辑上的“或”,用于组合多个模式。
  7. 分组和捕获

    • ():用于将多个模式组合成一个整体,或者捕获匹配的子串。
  8. 特殊字符

    • \d:匹配任何一个数字字符,等效于 [0-9]
    • \w:匹配任何一个字母数字字符,等效于 [a-zA-Z0-9_]
    • \s:匹配任何一个空白字符,包括空格、制表符、换行符等。
    • \b:匹配单词边界。
    • \:转义字符,用于匹配特殊字符本身。

这些是正则表达式中常用的一些匹配规则,可以根据具体的需求使用不同的规则组合来构建复杂的匹配模式。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值