📜  C++17 中的类 std::string_view

📅  最后修改于: 2021-09-07 02:21:37             🧑  作者: Mango

std::字符串 有一些缺点,最常见的情况之一是常量字符串。下面的程序演示了使用 std:: 字符串处理常量字符串时出现的问题:

方案一:

C++
// C++ program to demonstrate the
// problem occurred in string
#include 
#include 
using namespace std;
  
// Driver Code
int main()
{
    char str_1[]{ "Hello !!, GeeksforGeeks" };
  
    string str_2{ str_1 };
    string str_3{ str_2 };
  
    // Print the string
    cout << str_1 << '\n'
         << str_2 << '\n'
         << str_3 << '\n';
  
    return 0;
}


C++
// C++ program to implement
// the above approach
#include 
using namespace std;
#include 
  
// Driver code
int main()
{
    // View the text "hello", which is
    // stored in the binary
    string_view str_1{ "Hello !!, GeeksforGeeks" };
  
    // View of the same "hello"
    string_view str_2{ str_1 };
  
    // View of the same "hello"
    string_view str_3{ str_2 };
  
    std::cout << str_1 << '\n ' << str_1 << '\n ' << str_3 << '\n';
    return 0;
}


C++
// C++ program for the above approach
#include 
#include 
#include 
using namespace std;
  
string_view sur_name(wstring_view x)
{
    return x.substr(6);
}
  
// Driver Code
int main()
{
    // Basic_string_view wstr
    // (L"Madhav_Mohan"); both are equivalent
    const wchar_t* str1{ L"Madhav_Mohan" };
  
    cout << your_name(str1);
  
    return 0;
}


C++
// C++ program to implement
// the above approach
#include 
#include 
using namespace std;
  
string to_alpha(int r)
{
    if (r < 0)
        return " less than ";
    else if (r == 0)
        return " equal to ";
    else
        return " greater than ";
}
  
// Driver code
int main()
{
    char str{ "Muddy" };
    char str_1{ "Maddy" };
  
    string_view str_2{ str_1 };
  
    // This line prints Maddy
    cout << str_2 << '\n';
  
    // Change 'M' to 'D' in arr
    str_1[0] = 'D';
  
    // This line prints Daddy
    cout << str_2 << '\n';
  
    cout << str_2.at(2);
    cout << str_2.back();
    cout << " is last char of str_2 is y " << (str_2.back() == 's');
    cout << " is last char of str_2 is y " << boolalpha << (str_2.back() == 's');
  
    // In above statement boolalpha is
    // used to  give the boolean result
    // in True or False
  
    int cmp{ str_1.compare(str_2) };
  
  cout << to_alpha(cmp
};
int cmp_1 = str.compare(1, 4, str_2);
cout << "last three characters of str are" << to_alpha(cmp_1) << "str_2.\n";
return 0;
}


C++
// C++ program to implement
// the above approach
#include 
#include 
#include 
using namespace std;
  
// Driver code
int main()
{
    string_view str_1{ "Maddy" };
    cout << str_1 << '\n';
  
    string_view str_2{ "Daddy" };
    cout << str_2 << '\n';
  
    string_view var{ "Hello !!, GeeksforGeeks" };
    cout << var << '\n';
  
    cout << var.starts_with("Hi !!") << '\n';
    cout << var.ends_with("GeeksforGeeks !!") << '\n';
  
    // Remove the first characters.
    str_1.remove_prefix(1);
    cout << str_1 << '\n';
  
    // Remove the last 2 characters.
    str_1.remove_suffix(2);
    std::cout << str_1 << '\n';
  
    return 0;
}


C++
// C++ program to implement the
// above approach
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
  
// Driver code
int main()
{
    constexpr string_view str{ "Daddy" };
    constexpr string_view str1{ "Maddy" };
    string str2{};
  
    // Copy data of str1 into str2
    str1.copy(str2.data(), 4);
  
    // This line prints Madddy
    cout << str2.data() << '\n';
  
    if ((str.compare(str2)) != 0)
        cout << str << " "
             << "doesn't = to"
             << " " << str1 << '\n';
    cout << str1.starts_with("Mad");
    cout << '\n'
         << str.starts_with("Pad") << '\n';
    cout << str1.ends_with("ddt");
    cout << '\n'
         << str.ends_with("ddy") << '\n';
  
    // Checks whether the given string contains
    // the given substring or character
    constexpr string_view s1{ "Hello! Madhav" };
  
    // Position of the first character that
    // match first
    constexpr string_view str3{ "Hi! Maddy" };
  
    size_t found = str3.find(str1);
    cout << found << '\n';
  
    // Find the last occurrence of a substring
    cout << '\n'
         << str3.rfind('d');
  
    // Gives the first time occured char's position
    cout << '\n'
         << str3.find_first_of("d");
  
    // Gives the last time occured char's position
    cout << '\n'
         << str3.find_last_of("d");
  
    // Finds the first char that's not equal to
    // any of the char(s) in the given string
    cout << '\n'
         << str3.find_last_not_of(str, 6);
  
    // It givesIndex of first unmatched character
    // when successful or string::npos if no such
    // character found. It searches for the first
    // char that doesn't match any of the char(s)
    // that has given in the arguement
    cout << '\n'
         << str3.find_last_not_of(str, 4);
  
    return 0;
}


C++
// C++ program to implement
// the above approach
#include 
  
// For std::size
#include 
#include 
using namespace std;
  
// Driver code
int main()
{
    // No null-terminator.
    char name[]{ 'M', 'a', 'd',
                 'h', 'a', 'v' };
  
    // Here name isn't null-terminated.
    // We need to pass the length manually.
    // Because name is an array, we can
    // use std::size to get its length.
    string_view str_1{ \name, size(name) };
  
    // This is safe and cout knows how to
    // print std::string_views.
    cout << str_1 << '\n';
  
    return 0;
}


C++
// C++ program to implement
// the above approach
#include 
#include 
#include 
using namespace std;
  
void print(string s)
{
    cout << s << '\n';
}
  
// Driver code
int main()
{
    string_view str_1{ "Madhav" };
    str_1.remove_suffix(3);
  
    // compile error: won't implicitly
    // convert
    // print(str_1);
  
    // explicit conversion
    string str_2{ str_1 };
  
    print(str_2);
  
    // It'll work
    print(static_cast(str_1));
  
    return 0;
}


C++
// C++ program to implement
// the above approach
#include 
#include 
#include 
#include 
  
// Driver code
int main()
{
    string_view str_1{ "Madhav" };
    str_1.remove_suffix(3);
  
    // Create a std::string from
    // the std::string_view
    string str_2{ str_1 };
  
    // A Null_terminated C-style string.
    auto Null_Terminated{ str_2.c_str() };
  
    // Pass the null-terminated string
    // to the function that we want to use.
    cout << str_2 << " has " << strlen(Null_Terminated) << " letter's\n";
  
    return 0;
}


C++
// C++ program to implement
// the above approach
#include 
#include 
#include 
using namespace std;
  
string_view ask_name()
{
    cout << "Write your surname?\n";
  
    // A std::string is needed, because
    // std::cin needs to modify it.
    string str_1{};
    cin >> str_1;
  
    // Pass str_1 to std::string_view
    string_view str_2{ str_1 };
  
    cout << str_2 << '\n';
  
    // str_1 dies, and so does the
    // string that str created.
    return str_2;
}
  
// Driver code
int main()
{
    std::string_view str_2{ ask_name() };
  
    // str_2 is observing a string that
    // already died.
  
    // Undefined behavior you'll observe
    cout << your surname is << str_2 << '\n';
  
    return 0;
}


C++
// C++ program to implement
// the above approach
#include 
#include 
#include 
using namespace std;
#include 
  
// Driver code
int main()
{
    string str{ "Madhav_Mohan" };
    string_view str1{ str.data() };
    cout << str1 << endl;
    string_view str2{ str1.substr(6) };
    string_view str3{ str2 };
    cout << str3;
    return 0;
}


输出:

输出#1

说明:输出与预期相同。但是,为了两次查看“Hello !!, GeeksforGeeks”std::字符串在内存上执行了两次开销。但这里的任务是读取字符串(“Hello !!, GeeksforGeeks”) ,不需要对其进行写操作。所以只是为了显示一个字符串为什么要多次分配内存。为了更有效地处理字符串,C++17 提出了std::string_view() ,它提供了预定义 char str[] 的视图,而无需向内存创建新对象。

std:: 字符串 的缺点在上面的例子中,相同的字符串str_1通过分配两个不同的字符串变量被打印两次。因此,对于变量str_2str_3 ,内存是使用静态内存分配分配的,这会导致我们的内存开销两次。

std::string_view 的好处

  • 轻便且便宜: std::string_view非常轻便、便宜,主要用于提供字符串的视图。每当创建string_view 时,都不需要以上述示例中的方式复制字符串,这种方式效率低下并导致内存开销。它使字符串的复制过程非常有效,并且当在查看的字符串中进行修改时,永远不会创建字符串的任何副本,所做的更改已出现在std::string_view 中
  • 更好的性能: std::string_view比 const std::string&更好因为它消除了在字符串开头有std:: 字符串对象的约束,因为std::string_view由两个元素组成,第一个是const char*指向数组的起始位置,第二个是size
  • 支持必不可少的函数:标准:: string_view支持大部分是被施加在STD所有关键函数::字符串如SUBSTR,比较,发现,重载运算符(如==,<,> =!)。因此,在大多数情况下,当我们的首选项为只读时,它消除了具有 std:: 字符串对象声明的约束。

std::string_view C++17 库提出了一种标准类型的字符串( std::string_view ),它不同于通常的 std:: 字符串。

  • std :: string_view提供了一种轻量级对象,提供只读字符串或字符串的一部分的访问使用类似于标准:: _串的接口的接口,并且仅仅是指连续的炭序列。与 std:: 字符串保留自己的字符串副本不同,它还提供了在源代码中其他地方定义的字符串的视图。
  • 它由两个成员组成:指向 char 数组开头的const char*和 _size。它是对字符串本身的非拥有引用。
  • 在header(#include )中定义,std::string_view类模板如下:

以下是使用std::string_view的上述源代码的确切版本:

方案二:

C++

// C++ program to implement
// the above approach
#include 
using namespace std;
#include 
  
// Driver code
int main()
{
    // View the text "hello", which is
    // stored in the binary
    string_view str_1{ "Hello !!, GeeksforGeeks" };
  
    // View of the same "hello"
    string_view str_2{ str_1 };
  
    // View of the same "hello"
    string_view str_3{ str_2 };
  
    std::cout << str_1 << '\n ' << str_1 << '\n ' << str_3 << '\n';
    return 0;
}

输出-

输出#1

说明:输出将与上面相同,但不会在内存中创建字符串“Hello !!, GeeksforGeeks”的更多副本。

std::string_view 中的参数

字符类型:
char类型定义了存储在string_view字符的类型。 C++ 标准库为上述模板的特化提供了以下类型定义。

  1. string_view用于char类型的元素
  2. wstring_view ,用于wchar_t
  3. u16string_view用于char16_t
  4. u32string_view用于char32_t

下面是说明 Char 类型的 C++ 程序:

方案三:

C++

// C++ program for the above approach
#include 
#include 
#include 
using namespace std;
  
string_view sur_name(wstring_view x)
{
    return x.substr(6);
}
  
// Driver Code
int main()
{
    // Basic_string_view wstr
    // (L"Madhav_Mohan"); both are equivalent
    const wchar_t* str1{ L"Madhav_Mohan" };
  
    cout << your_name(str1);
  
    return 0;
}

输出-

输出#4

std::string_view示例下面的示例表明,大多数基于字符串的函数都可以与 std::string_view 一起使用,如str.compare()str.back()str.cend()str 。 at()函数。

程序4:

C++

// C++ program to implement
// the above approach
#include 
#include 
using namespace std;
  
string to_alpha(int r)
{
    if (r < 0)
        return " less than ";
    else if (r == 0)
        return " equal to ";
    else
        return " greater than ";
}
  
// Driver code
int main()
{
    char str{ "Muddy" };
    char str_1{ "Maddy" };
  
    string_view str_2{ str_1 };
  
    // This line prints Maddy
    cout << str_2 << '\n';
  
    // Change 'M' to 'D' in arr
    str_1[0] = 'D';
  
    // This line prints Daddy
    cout << str_2 << '\n';
  
    cout << str_2.at(2);
    cout << str_2.back();
    cout << " is last char of str_2 is y " << (str_2.back() == 's');
    cout << " is last char of str_2 is y " << boolalpha << (str_2.back() == 's');
  
    // In above statement boolalpha is
    // used to  give the boolean result
    // in True or False
  
    int cmp{ str_1.compare(str_2) };
  
  cout << to_alpha(cmp
};
int cmp_1 = str.compare(1, 4, str_2);
cout << "last three characters of str are" << to_alpha(cmp_1) << "str_2.\n";
return 0;
}

输出:

输出#2

std::string_view 的修改在 C++20 中添加了一些新函数,如 str.remove_suffix(),使用这些函数的 str.remove_suffix() 可以修改 std::string_view。 Like 可以删除给定字符串的选择性后缀或前缀。

计划5:

C++

// C++ program to implement
// the above approach
#include 
#include 
#include 
using namespace std;
  
// Driver code
int main()
{
    string_view str_1{ "Maddy" };
    cout << str_1 << '\n';
  
    string_view str_2{ "Daddy" };
    cout << str_2 << '\n';
  
    string_view var{ "Hello !!, GeeksforGeeks" };
    cout << var << '\n';
  
    cout << var.starts_with("Hi !!") << '\n';
    cout << var.ends_with("GeeksforGeeks !!") << '\n';
  
    // Remove the first characters.
    str_1.remove_prefix(1);
    cout << str_1 << '\n';
  
    // Remove the last 2 characters.
    str_1.remove_suffix(2);
    std::cout << str_1 << '\n';
  
    return 0;
}

输出:

输出#3

下面是一些可以用 std::string_view 执行的其他操作的例子:

计划6:

C++

// C++ program to implement the
// above approach
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
  
// Driver code
int main()
{
    constexpr string_view str{ "Daddy" };
    constexpr string_view str1{ "Maddy" };
    string str2{};
  
    // Copy data of str1 into str2
    str1.copy(str2.data(), 4);
  
    // This line prints Madddy
    cout << str2.data() << '\n';
  
    if ((str.compare(str2)) != 0)
        cout << str << " "
             << "doesn't = to"
             << " " << str1 << '\n';
    cout << str1.starts_with("Mad");
    cout << '\n'
         << str.starts_with("Pad") << '\n';
    cout << str1.ends_with("ddt");
    cout << '\n'
         << str.ends_with("ddy") << '\n';
  
    // Checks whether the given string contains
    // the given substring or character
    constexpr string_view s1{ "Hello! Madhav" };
  
    // Position of the first character that
    // match first
    constexpr string_view str3{ "Hi! Maddy" };
  
    size_t found = str3.find(str1);
    cout << found << '\n';
  
    // Find the last occurrence of a substring
    cout << '\n'
         << str3.rfind('d');
  
    // Gives the first time occured char's position
    cout << '\n'
         << str3.find_first_of("d");
  
    // Gives the last time occured char's position
    cout << '\n'
         << str3.find_last_of("d");
  
    // Finds the first char that's not equal to
    // any of the char(s) in the given string
    cout << '\n'
         << str3.find_last_not_of(str, 6);
  
    // It givesIndex of first unmatched character
    // when successful or string::npos if no such
    // character found. It searches for the first
    // char that doesn't match any of the char(s)
    // that has given in the arguement
    cout << '\n'
         << str3.find_last_not_of(str, 4);
  
    return 0;
}

输出:

输出#4

非空终止字符串与 C 字符串和 std::字符串 需要一个字符串终止符 (‘\0’) 在字符串的末尾, std::string_view 不需要空终止符来标记字符串。因为它会跟踪字符串长度。

下面是实现上述方法的 C++ 程序:

程序 7:

C++

// C++ program to implement
// the above approach
#include 
  
// For std::size
#include 
#include 
using namespace std;
  
// Driver code
int main()
{
    // No null-terminator.
    char name[]{ 'M', 'a', 'd',
                 'h', 'a', 'v' };
  
    // Here name isn't null-terminated.
    // We need to pass the length manually.
    // Because name is an array, we can
    // use std::size to get its length.
    string_view str_1{ \name, size(name) };
  
    // This is safe and cout knows how to
    // print std::string_views.
    cout << str_1 << '\n';
  
    return 0;
}

输出:

输出#5

将 std::string_view 转换为 std:: 字符串需要使用显式转换方法,因为隐式转换在这里无济于事。

下面是实现 std::string_view 到 std:: 字符串转换的 C++ 程序:

程序8:

C++

// C++ program to implement
// the above approach
#include 
#include 
#include 
using namespace std;
  
void print(string s)
{
    cout << s << '\n';
}
  
// Driver code
int main()
{
    string_view str_1{ "Madhav" };
    str_1.remove_suffix(3);
  
    // compile error: won't implicitly
    // convert
    // print(str_1);
  
    // explicit conversion
    string str_2{ str_1 };
  
    print(str_2);
  
    // It'll work
    print(static_cast(str_1));
  
    return 0;
}

输出:

输出#6

将 std::string_view 转换为 C 风格的字符串: strlen() 等函数,这些函数需要一个 C 风格的字符串才能使用。因此,每当需要将 std::string_view 转换为 C 风格的字符串,都可以通过首先转换为 std:: 字符串来完成。

下面是实现上述方法的 C++ 程序:

程序 9:

C++

// C++ program to implement
// the above approach
#include 
#include 
#include 
#include 
  
// Driver code
int main()
{
    string_view str_1{ "Madhav" };
    str_1.remove_suffix(3);
  
    // Create a std::string from
    // the std::string_view
    string str_2{ str_1 };
  
    // A Null_terminated C-style string.
    auto Null_Terminated{ str_2.c_str() };
  
    // Pass the null-terminated string
    // to the function that we want to use.
    cout << str_2 << " has " << strlen(Null_Terminated) << " letter's\n";
  
    return 0;
}

输出:

输出#7

范围的问题性病:: string_view:std :: string_view提供的视图,因此它保持独立,其中它提供了一个视图的字符串的,并且除非观看字符串是在范围,而是在情况下,如果它伸出的字符串的视图查看的字符串范围消失,然后 std::string_view 没有任何可投影的内容,因此它显示未定义的行为。 std::string_view 总是需要一个或已经由STD创建一个字符串,字符串:: 在范围内以投影字符串的视图,但是一旦范围终止 std::string_view 就无法自行创建任何字符串,因为它只是一个视图。

下面是实现上述方法的示例:

计划10:

C++

// C++ program to implement
// the above approach
#include 
#include 
#include 
using namespace std;
  
string_view ask_name()
{
    cout << "Write your surname?\n";
  
    // A std::string is needed, because
    // std::cin needs to modify it.
    string str_1{};
    cin >> str_1;
  
    // Pass str_1 to std::string_view
    string_view str_2{ str_1 };
  
    cout << str_2 << '\n';
  
    // str_1 dies, and so does the
    // string that str created.
    return str_2;
}
  
// Driver code
int main()
{
    std::string_view str_2{ ask_name() };
  
    // str_2 is observing a string that
    // already died.
  
    // Undefined behavior you'll observe
    cout << your surname is << str_2 << '\n';
  
    return 0;
}

输出:

输出#7

使用 data() 执行 std::string_view函数: data()函数将字符串的字符写入数组。它返回一个指向数组的指针,该指针是从字符串到数组的转换中获得的。它的返回类型不是有效的 C 字符串,因为在数组末尾没有附加 ‘\0’字符。

下面是实现上述方法的 C++ 程序:

计划 11:

C++

// C++ program to implement
// the above approach
#include 
#include 
#include 
using namespace std;
#include 
  
// Driver code
int main()
{
    string str{ "Madhav_Mohan" };
    string_view str1{ str.data() };
    cout << str1 << endl;
    string_view str2{ str1.substr(6) };
    string_view str3{ str2 };
    cout << str3;
    return 0;
}

输出:

输出#8

非成员函数:

operator==
   operator!=                                    (has been removed in C++20)
   operator<                                     (has been removed in C++20)
   operator>                                     (has been removed in C++20)
   operator<=                                    (has been removed in C++20)
   operator>=                                    (has been removed in C++20)
   operator<=>                                   (has newly been included C++20)

注意 Std::string_view 仅在 C++17 或以上版本中运行。有许多与 std::string_view 合并的函数,其中一些是新的(C++20),一些是旧的。在下面,我引用了 web_address,您可以在其中深入了解它们。

想要从精选的视频和练习题中学习,请查看C++ 基础课程,从基础到高级 C++ 和C++ STL 课程,了解基础加 STL。要完成从学习语言到 DS Algo 等的准备工作,请参阅完整的面试准备课程