Python与c++语法比较 字符串篇

 
Category: C_C++

写在前面

刷lc从Python转向C++, 不只是语法层面, 还要改变很多的API, 这次记录一下C++和Python在字符串方面的一些区别, 供参考.

基本区别

Python字符串是不可变类型, 而C++的String为容器(本质上是一个类的别名, 说容器有点不合适, 因为容器内的元素类型已经被指定为char), 可变类型.

C++

先说一下字符, 是指char类型, 其本质上也是int类型(0~127的ASCII字符, 存在隐式类型转换)

using string = std::basic_string<char>;  // string其实是一个类型别名

如果不需要一般的字符串操作, 可以使用vector<char>, 是一种比较纯粹的字符数组类型, 资源占用较少. 并且C++17新增了一种叫做string_view的类型, 用起来比string轻量, 下面是罗剑锋老师给出的描述.

string它确实有点“重”,大字符串的拷贝、修改代价很高,所以我们通常都尽量用 const string&,但有的时候还是无法避免(比如使用 C 字符串、获取子串)。如果你对此很在意,就有必要找一个“轻量级”的替代品。

在 C++17 里,就有这么一个完美满足所有需求的东西,叫 string_view。顾名思义,它是一个字符串的视图,成本很低,内部只保存一个指针和长度,无论是拷贝,还是修改,都非常廉价。

Python

字符串是不可变类型, 并且转换成ASCII码的话需要额外的内置函数:

chr(97) # 'a'
ord('a') # 97

字符串创建

Python

s1='1'
s2='abc'
s3="45"
s4=r"D:\Desktop"

单双引号都可以, 并且支持原生(raw)字符串.

小坑:

原生字符串不支持单字符, 即s=r'\'这样的写法是会报错的, 只能老老实实转义:s='\\.

C++

string s1="123";
// string s2='1';// error
string s3{"abc"}; //initializer_list, C++11

单引号和双引号并不能混用, 需要注意单引号只能表示单一字符, char类型, 而双引号表示字符串类型(char*), 除非使用string以及相关构造函数, 否则默认的类型为const char*, 例如:

void t1() {
    auto s1 = "abc";
    cout << typeid(s1).name() << endl; // PKc

    auto s2 = "bcd"s; // 字符串字面量C++14
    cout << typeid(s2).name() << endl;
    string s3 = "bcd";
    cout << typeid(s3).name() << endl;
    /*NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
    NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE*/
}

并且C风格的字符串中双引号之间可以合并的语法也被C++的string延续了下来.

void t2() {
    string s1 =
        "abc"
        "ac";
    cout << s1 << endl;
    /* string s2 = 'a' 'b'; // error */
}

主要操作

操作 Python方法 C++方法
s1中查找s2 s1.find(s2),
找到返回索引, 否则返回-1
s1.find(s2), 找到返回索引,
否则返回string::npos
取子串[1,5] s[1:6], 左闭右开区间取值 s.substr(1, 5)表示从索引1开始取,
取包括1在内的5个字符
连接字符串
s1s2
加号或者+=连接 加号或者+=连接
字符串比较 字典序比较, 使用比较运算符 字典序比较, 使用比较运算符

查找

Python比较简单, 找不到返回-1

s = "abcde"

print(s.find("a"))  # 0
print(s.find("z"))  # -1

print(s.rfind("a"))  # 0
print(s.rfind("z"))  # -1

C++就不一样了, 需要一个npos标志

int main() {
    //
    string s{"abcde"s};
    // substr(i, k)表示从i开始(包括i位置的字符)往后取k个字符
    cout << s.find('a') << endl;  // 0
    cout << s.find('y') << endl;  // 18446744073709551615, equal to 2^64 − 1
    cout << ULONG_MAX << endl;    // 18446744073709551615
    cout << string::npos << endl; // 18446744073709551615
}

取子串

Python, 就像数组一样, 比较优雅(左闭右开区间)

s1 = "abcde"

#  取从头开始向后的三个字符
print(s1[0:3])  # abc
print(s1[:3])  # abc
print(s1[:-2])  # abc

#  取第2个到第四个字符
print(s1[1:4])  # bcd
print(s1[-4:-1])  # bcd

# 反转
print(s1[::-1])  # edcba

C++需要substr成员函数, 而且返回的还是字符串的拷贝, 比较耗内存:

int main() {
    string s{"abcde"s};
    // substr(i, k)表示从i开始(包括i位置的字符)往后取k个字符
    cout << s.substr(2, 0) << endl; //
    cout << s.substr(2, 1) << endl; // c
    cout << s.substr(2, 2) << endl; // cd
    cout << s.substr(2) << endl;    // cde
}

需要注意substr方法的重载版本, 单参数情况是从参数位置到字符串末尾.

检查字符

条件
Python
(成员函数,可针对字符串)
C++
(全局函数,只针对字符)
是否为数字 s.isdigit() isdigit(c)
是否为字母 s.isalpha() isalpha(c)
是否为小写字母 s.islower() islower(c)
是否为大写字母 s.isupper() isupper(c)
是否为字母或数字 s.isalnum() isalnum(c)
是否为十六进制字符(0~F) - isxdigit(c)

转换字符大小写:

转换方向 Python(成员函数) C++(全局函数)
大写->小写 s.lower() static_cast<char>(tolower(c))
小写->大写 s.upper() static_cast<char>(toupper(c))

C++

#include <cctype>

void t1() {
    char c1 = 'a';
    cout << islower(c1) << endl; // 1
    char c2 = 'N';
    cout << isupper(c2) << endl; // 1
    char c3 = '0';
    cout << isnumber(c3) << endl; // 1
    char c4 = '0';
    cout << isalnum(c4) << endl; // 1
    char c5 = 'g';
    cout << isxdigit(c5) << endl; // 0
}

void t2() {
    char c1 = 'a';
    /* cout << (char)toupper(c1) << endl; // A */
    cout << static_cast<char>(toupper(c1)) << endl; // A
    char c2 = 'A';
    cout << (char)tolower(c2) << endl; // a
}

格式化输出

这部分不是严格的字符串方面, 但是也有值得对比的点.

C++当然要用最具C++味道的cout了(虽然名声不好, 但是在泛型方面的支持还是比printf()方便不少, 注意一下别和左移运算符用混了就行), 而Python就是一个printf()吃遍天.

输出的内容 Python C++
字符串 print(s) cout<<s<<endl;
二进制整数(字符串形式输出) print(bin(x)) cout<<bitset<32>(x);
八进制整数 print(oct(x)) cout<<oct<<x<<endl;
十六进制整数(地址值) print(hex(x)) cout<<hex<<x<<endl;
整数格式化(指定对齐方式和位数) Python3.6支持的f-string
print(f"{123:0<6}")
cout << setfill('0')
<< setw(6) << left
<< 123 << endl;
浮点数格式化输出(指定小数点后4位) print(f"{1.2:.4f}") cout << fixed
<< setprecision(4)
<< 3.14159 << endl;
     

参考: Formatting Output in C++ (niu.edu);

C++

两种方法.

#include<sstream>
// 使用字符流. (可以定制)
void t3() {
    /* basic_ostringstream<char> oss; */
    ostringstream oss;
    // 默认是right
    oss << setfill('0') << setw(5) << left << 123;
    cout << oss.str() << endl;
    // 可定制
    cout << oss.str().insert(3, "-").insert(5, "-") << endl;
    /* 12300 */
    /* 123-0-0 */
}
// 直接输出
void t4() {
    // 默认是right
    cout << setfill('0') << setw(5) << left << 123 << endl;
    /* 12300 */
}
// 浮点数
void t5() {
    cout << setprecision(3) << 3.14159 << endl; // 3.14,总位数
    cout << fixed << setprecision(3) << 3.14159 << endl; // 3.142,小数点后位数,支持四舍五入
}

Python

print(f"{123:0<6}") # 123000
print(f"{1.23456:.4f}") # 支持四舍五入, 1.2346

字符流刷新

这算是一个比较小的主题, 但是也很常用. 一个比较常见的例子就是终端命令行程序中的进度条旋转, 使用字符流的输出刷新flush就可以完成.

下面是Python和C++的实现方式, 需要知道的另外一个点就是\r表示光标移动到开头, 这同时也是进度条旋转的关键.

from time import sleep

str_lst = ["-", "\\", "|", "/", "-"]

for i in range(100):
    sleep(0.5)
    print(f"{str_lst[i%4]} process: {i}%\r", flush=True, end="")

aa

下面是C++版本的:

#include <unistd.h>
#include <string>
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {
    string str{R"(-\|/-)"s};
    int n{100};
    while (n--) {
        usleep(200 * 1000); // 微秒
        cout << str[n % 4] << " process:" << 100 - n << "%\r" << flush;
    }
    return 0;
}

aa

纯 C 实现也可以:

#include <stdio.h>
#include <unistd.h> // for usleep()

int main(int argc, char *argv[]) {
    char str[] = "-\\|/-";
    int n = 100;
    while (n--) {
        usleep(200000); // in microseconds
        printf("%c process:%d%%\r", str[n % 4], 100 - n);
        fflush(stdout);
    }
    return 0;
}

类型转换

转换方向 Python函数 C++函数
字符串->整数 int(),
支持不同数制(base参数)
stoi(), stol(), stoll()
字符串->浮点数 eval() stof(), stod()
浮点数/整数->字符串 str() to_string()

Python的字符串转换还是相当方便的, 并且支持浮点数

Python

int('1011', base=2) # 11

C++

void t1() {
    auto a = "123"s;
    auto b = "12.3"s;
    cout << stoi(a) << endl;
    cout << stod(b) << endl;
    /* 123 */
    /* 12.3 */
}

小结

暂时先总结这么多, 之后刷题遇到再说.