模拟实现 string 类的一些常用函数

news2024/11/15 17:32:30

目录

构造函数

析构函数

拷贝构造

赋值重载

迭代器( begin() 和 end() )

运算符重载流插入( operator << ( ) )

size()

capacity()

运算符重载operator[ ]

clear()

reserve ( )

push_back ( )

append ( )

运算符重载 operator += ( )

insert ( )

erase ( )

find ( )

substr ( )

 c_str( )

比较大小相关函数

运算符重载流提取( operator >> ( ) )

完整源代码

String.h

String.c

StringTest.c


看一下string类常用函数的实现。其本质还是 string 类还是通过顺序表来进行存储的,其通过三个私有成员变量来存储字符串信息。

class string
{

private:
    char* _str;
    size_t _capacity;
    size_t _size;
}

构造函数

由于字符串要以 '\0' 结尾,即使字符串长度为 0 时依旧还要储存 '\0' ,所以当其在初始化列表初始化时,_str不能初始化为 nullptr ,要开辟空间存储 '\0' 。

string()
    :_str(new char[1]{'\0'})
    ,_size(0)
    ,_capacity(0)
{

}

带参的构造函数

string(const char* str)
{
    _size = strlen(str);
    _capacity = _size;

    //多出的一个空间用于存储'\0'
    _str = new char[_capacity + 1];

    //将str的值拷贝给_str
    strcpy(_str, str);
}

也可以加上缺省值,如空字符串。

string(const char* str = "")
{
    _size = strlen(str);
    _capacity = _size;

    //多出的一个空间用于存储'\0'
    _str = new char[_capacity + 1];

    // 将str的值拷贝给_str
    strcpy(_str, str);
}

可以看到,上方的代码实现了默认构造和带参构造的功能,将两者结合为在一起。

析构函数

要释放为 _str 开辟的空间。

~string()
{
    delete[] _str;
    _str = nullptr;
    _capacity = _size = 0;
}

拷贝构造

由于 _str 需要开辟空间,所以拷贝构造需要手动实现深拷贝。

string(const string& s)
{
    _str = new char[s._capacity + 1];
    strcpy(_str, s._str);

    _capacity = s._capacity;
    _size = s._size;
}

赋值重载

赋值重载函数和拷贝构造相同,也需要深拷贝。

string& operator=(const string& s)
{
    //要注意,如果是自己和自己赋值,就不能释放空间,直接返回即可
    if (this != &s)
    {
        //先将原来的开辟的空间释放
        delete[] _str;

        //再重新开辟与s._str相同大小的空间
        _str = new char[s._capacity + 1];
        strcpy(_str, s._str);
        
        _capacity = s._capacity;
        _size = s._size;
    }
    return *this;
}

迭代器( begin() 和 end() )

迭代器功能强大,实现其也很复杂,但如果要简单模拟迭代器,却十分简单。在string 类中,其会返回字符串起始位置和结尾位置的下一位,其用法类似于指针。所以要简单模拟迭代器,我们可以使用指针来实现。

// 将类型重命名
typedef char* iterator;

iterator begin()
{
    //返回起始位置的指针
    return _str;
}

iterator end()
{
    //返回结尾位置的下一位
    return _str + _size;
}

还可以实现一个 const 版本的迭代器。

typedef const char* const_iterator;

const_iterator begin() const
{
    return _str;
}

const_iterator end() const
{
    return _str + _size;
}

运算符重载流插入( operator << ( ) )

其要实现传入一个实例化的对象就可以把其存储的字符串打印在屏幕上。可以通过循环解决。

ostream& operator<<(ostream& out, const string& s)
{
    //通过遍历每个字符,把字符打印出来
    for (auto ch : s)
    {
        out << ch;
    }
    return out;
}

size()

返回 string 类对象的 _size 。

size_t size() const
{
    return _size;
}

capacity()

返回 string 类对象的 _capacity 。

size_t capacity() const
{
    return _capacity;
}

运算符重载operator[ ]

取得第 i 位置元素的值。

char& operator[](size_t pos)
{
    return _str[pos];
}
const char& operator[](size_t pos) const
{
    return _str[pos];
}

clear()

要清空数据,可以直接把第一个位置置为 '\0' ,同时把 _size 置为 0 即可。

void clear()
{
    _str[0] = '\0';
    _size = 0;
}

reserve ( )

用于开辟一块空间,常用于扩容。

void string::reserve(size_t n)
{
    if (n > _capacity)
    {
        //先开辟一块与_str大小相同的空间
        char* tmp = new char[n + 1];

        //拷贝_str的数据
        strcpy(tmp, _str);

        //销毁原来的空间
        delete[] _str;

        //再把重新开辟的空间指向_str,并改变容量
        _str = tmp;
        _capacity = n;
    }
}

push_back ( )

在尾部插入一个字符。

//在尾部插入一个字符
void string::push_back(char c)
{
    //考虑空间是否充足,是否要扩容
    if (_capacity == _size)
    {
        //判断_capacity是否为0
        reserve(_capacity == 0 ? 4 : 2 * _capacity);
    }

    //注意,此时要插入的字符将原来的'\0'覆盖,后面要手动添加'\0'
    _str[_size] = c;
    _size++;

    //要手动添加'\0',
    _str[_size] = '\0';
}

append ( )

在尾部插入一串字符。

//在尾部插入一串字符
void string::append(const char* str)
{
    size_t len = strlen(str);

    if (_size + len > _capacity)
    {
        //如果要_size + len > 2 * _capacity,就按_size + len扩容,如果没有,就按二倍扩容
        reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
    }

    注意 i <= len,要将'\0'也拷贝进去
    //for (size_t i = 0; i <= len; i++)
    //{
    //    _str[_size + i] = str[i];
    //}
    
    //可以直接用strcpy代替,其会把'\0'也拷贝进去
    strcpy(_str + _size, str);

    _size += len;
}

运算符重载 operator += ( )

直接 += 一个或一串字符。可以直接复用 push_back( ) 和 append( ) 。

//+= 一个字符
string& string::operator+=(char c)
{
    push_back(c);
    return *this;
}

//+= 一串字符
string& string::operator+=(const char* str)
{
    append(str);
    return *this;
}

insert ( )

在任意位置插入 1 个字符。

//在pos位置上插入字符c
void string::insert(size_t pos, char c)
{
    //先看是否扩容
    if (_capacity == _size)
    {
        reserve(_capacity == 0 ? 4 : 2 * _capacity);
    }

    //将'\0'也向后挪一位,同时有效防止pos=0出现数组越界的情况
    size_t end = _size + 1;
    while (end > pos)
    {
        _str[end] = _str[end - 1];
        end--;
    }

    _str[pos] = c;
    _size++;
}

在任意位置插入 n 个字符。

//在pos位置上插入字符串str
void string::insert(size_t pos, const char* str)
{
    size_t len = strlen(str);
    if (_size + len > _capacity)
    {
        reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
    }

    size_t end = _size + len;
    while (end > pos + len - 1)
    {
        _str[end] = _str[end - len];
        end--;
    }

    for (size_t i = 0; i < len; i++)
    {
        _str[pos + i] = str[i];
    }
    _size += len;
}

erase ( )

从任意位置删除 n 个数据,如果没有指定长度,默认删除到结尾。

// 删除pos位置开始的 len 个元素
void string::erase(size_t pos, size_t len)
{
    //如果从pos位置开始,要删除的数据大于剩余长度,
    // 就说明从pos位置开始的字符都要删除
    if (len >= _size - pos)
    {
        //可以直接把pos位置置为'\0'
        _str[pos] = '\0';
        _size = pos;
    }
    else
    {
        //如果不是,就把后面的一次向前挪动,
        // 注意 i <= _size,'\0'也要挪动
        for (size_t i = pos + len; i <= _size; i++)
        {
            _str[i - len] = _str[i];
        }
        _size -= len;
    }
}

find ( )

查找字符或字符串第一次出现的位置,并返回下标,如果没有找到,就返回npos。

// 返回c在string中第一次出现的位置
size_t string::find(char c, size_t pos) const
{
    for (size_t i = pos; i < _size; i++)
    {
        if (_str[i] == c)
        {
            return i;
        }
    }
    return npos;
}

查找一串字符,可以用C语言的库函数 strstr( ),也可以自己手动实现查找,(具体实现看这篇文章: 字符串匹配算法)。

// 返回子串s在string中第一次出现的位置
size_t string::find(const char* s, size_t pos) const
{
    //可以直接使用C语言的库函数strstr()
    const char* str = strstr(_str + pos, s);
    //如果未找到相应的子串,就会返回 nullptr
    if (str == nullptr)
    {
        return npos;
    }
    else
    {
        return str - _str;
    }
}

substr ( )

从 pos 位置开始的 n 个字符构造子串,并返回。如果没有指定 n ,就默认到结尾。

string string::substr(size_t pos, size_t len)
{
    if (len >= _size - pos)
    {
        len = _size - pos;
    }

    string sub;
    //预先开好空间,减少扩容
    sub.reserve(len);

    for (size_t i = 0; i < len; i++)
    {
        sub += _str[pos + i];
    }
    return sub;
}

 c_str( )

其是string类构造的一个函数,用于返回指向数组的指针,也就是说它返回当前字符串的首字符地址。

其实现也很简单。

//返回字符串第一个节点的位置
const char* c_str() const
{
    return _str;
}

比较大小相关函数

比较字符串,可以使用C语言中的 strcmp() 函数。

bool operator<(const string& s1, const string& s2)
{
    return strcmp(s1.c_str(), s2.c_str()) < 0;
}

bool operator<=(const string& s1, const string& s2)
{
    return s1 < s2 || s1 == s2;
}

bool operator>(const string& s1, const string& s2)
{
    return !(s1 <= s2);
}

bool operator>=(const string& s1, const string& s2)
{
    return !(s1 < s2);
}

bool operator==(const string& s1, const string& s2)
{
    return strcmp(s1.c_str(), s2.c_str()) == 0;
}

bool operator!=(const string& s1, const string& s2)
{
    return !(s1 == s2);
}

运算符重载流提取( operator >> ( ) )

先来看代码:

​
istream& operator>>(istream& in, string& s)
{
    //重新输入数据之前要先把之前的数据清空
    s.clear();

    char ch;
    in >> ch;

    while (ch != ' ' && ch != '\n')
    {
        s += ch;
        in >> ch;
    }

    return in;
}

会发现其无论如何输入,都不会停止。这是因为在输入时,in 会自动识别并跳过空格和换行,但我们要通过空格和换行来判断循环是否结束,所以导致陷入死循环。因此,我们要使用 istream 类当中的 get() 函数。

istream& operator>>(istream& in, string& s)
{
    //重新输入数据之前要先把之前的数据清空
    s.clear();

    char ch;
    //in >> ch;
    ch = in.get();

    while (ch != ' ' && ch != '\n')
    {
        s += ch;
        //in >> ch;
        ch = in.get();
    }

    return in;
}

完整源代码

String.h

#include<iostream>
#include<assert.h>
using namespace std;

namespace Friend
{
    class string
    {
    public:
        //构造函数
        /*string()
            :_str(new char[1]{'\0'})
            ,_size(0)
            ,_capacity(0)
        {

        }*/

        //string(const char* str)
        //{
        //    _size = strlen(str);
        //    _capacity = _size;

        //    //多出的一个空间用于存储'\0'
        //    _str = new char[_capacity + 1];

        //    //将str的值拷贝给_str
        //    strcpy(_str, str);
        //}

        string(const char* str = "")
        {
            _size = strlen(str);
            _capacity = _size;
            //多出的一个空间用于存储'\0'
            _str = new char[_capacity + 1];
            // 将str的值拷贝给_str
            strcpy(_str, str);
        }

        //拷贝构造s1(s2)
        string(const string& s)
        {
            _str = new char[s._capacity + 1];
            strcpy(_str, s._str);

            _capacity = s._capacity;
            _size = s._size;
        }

        //赋值重载s1 = s2
        string& operator=(const string& s)
        {
            if (this != &s)
            {
                //先将原来的开辟的空间释放
                delete[] _str;

                //再重新开辟与s._str相同大小的空间
                _str = new char[s._capacity + 1];
                strcpy(_str, s._str);
                
                _capacity = s._capacity;
                _size = s._size;
            }
            return *this;
        }

        //析构
        ~string()
        {
            delete[] _str;
            _str = nullptr;
            _capacity = _size = 0;
        }

        // 将类型重命名
        typedef char* iterator;

        iterator begin()
        {
            //返回起始位置的指针
            return _str;
        }

        iterator end()
        {
            //返回结尾位置的下一位
            return _str + _size;
        }

        typedef const char* const_iterator;

        const_iterator begin() const
        {
            return _str;
        }

        const_iterator end() const
        {
            return _str + _size;
        }

        //返回字符串第一个节点的位置
        const char* c_str() const
        {
            return _str;
        }

        size_t size() const
        {
            return _size;
        }

        size_t capacity() const
        {
            return _capacity;
        }
        
        char& operator[](size_t pos)
        {
            return _str[pos];
        }

        const char& operator[](size_t pos) const
        {
            return _str[pos];
        }

        void clear()
        {
            _str[0] = '\0';
            _size = 0;
        }

        //扩容
        void reserve(size_t n);

        void push_back(char c);

        void append(const char* str);

        string& operator+=(char c);

        string& operator+=(const char* str);

        // 在pos位置上插入字符c/字符串str,并返回该字符的位置
        void insert(size_t pos, char c);
        void insert(size_t pos, const char* str);

        // 删除pos位置上的元素,并返回该元素的下一个位置
        void erase(size_t pos, size_t len = npos);

        // 返回c在string中第一次出现的位置
        size_t find(char c, size_t pos = 0) const;

        // 返回子串s在string中第一次出现的位置
        size_t find(const char* s, size_t pos = 0) const;

        string substr(size_t pos = 0, size_t len = npos);

    private:
        char* _str;
        size_t _capacity;
        size_t _size;

        const static size_t npos;
    };

    bool operator<(const string& s1, const string& s2);
    bool operator<=(const string& s1, const string& s2);
    bool operator>(const string& s1, const string& s2);
    bool operator>=(const string& s1, const string& s2);
    bool operator==(const string& s1, const string& s2);
    bool operator!=(const string& s1, const string& s2);

    ostream& operator<<(ostream& out, const string& s);
    istream& operator>>(istream& in, string& s);
}

String.c

#include "String.h"

namespace Friend
{
    //扩容
    void string::reserve(size_t n)
    {
        if (n > _capacity)
        {
            char* tmp = new char[n + 1];

            strcpy(tmp, _str);

            delete[] _str;

            _str = tmp;
            _capacity = n;
        }
    }
     
    //在尾部插入一个字符
    void string::push_back(char c)
    {
        //考虑空间是否充足,是否要扩容
        if (_capacity == _size)
        {
            //判断_capacity是否为0
            reserve(_capacity == 0 ? 4 : 2 * _capacity);
        }

        //注意,此时要插入的字符将原来的'\0'覆盖,后面要手动添加'\0'
        _str[_size] = c;
        _size++;

        //要手动添加'\0',
        _str[_size] = '\0';
    }
    
    //在尾部插入一串字符
    void string::append(const char* str)
    {
        size_t len = strlen(str);

        if (_size + len > _capacity)
        {
            //如果要_size + len > 2 * _capacity,就按_size + len扩容,如果没有,就按二倍扩容
            reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
        }

        注意 i <= len,要将'\0'也拷贝进去
        //for (size_t i = 0; i <= len; i++)
        //{
        //    _str[_size + i] = str[i];
        //}
        
        //可以直接用strcpy代替,其会把'\0'也拷贝进去
        strcpy(_str + _size, str);

        _size += len;
    }

    //+= 一个字符
    string& string::operator+=(char c)
    {
        push_back(c);
        return *this;
    }

    //+= 一串字符
    string& string::operator+=(const char* str)
    {
        append(str);
        return *this;
    }

    //在pos位置上插入字符c
    void string::insert(size_t pos, char c)
    {
        //先看是否扩容
        if (_capacity == _size)
        {
            reserve(_capacity == 0 ? 4 : 2 * _capacity);
        }

        //将'\0'也向后挪一位,同时有效防止pos=0出现数组越界的情况
        size_t end = _size + 1;
        while (end > pos)
        {
            _str[end] = _str[end - 1];
            end--;
        }

        _str[pos] = c;
        _size++;
    }

    //在pos位置上插入字符串str
    void string::insert(size_t pos, const char* str)
    {
        size_t len = strlen(str);
        if (_size + len > _capacity)
        {
            reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
        }

        size_t end = _size + len;
        while (end > pos + len - 1)
        {
            _str[end] = _str[end - len];
            end--;
        }

        for (size_t i = 0; i < len; i++)
        {
            _str[pos + i] = str[i];
        }
        _size += len;
    }

    const size_t string::npos = -1;

    // 删除pos位置开始的 len 个元素
    void string::erase(size_t pos, size_t len)
    {
        //如果从pos位置开始,要删除的数据大于剩余长度,
        // 就说明从pos位置开始的字符都要删除
        if (len >= _size - pos)
        {
            //可以直接把pos位置置为'\0'
            _str[pos] = '\0';
            _size = pos;
        }
        else
        {
            //如果不是,就把后面的一次向前挪动,
            // 注意 i <= _size,'\0'也要挪动
            for (size_t i = pos + len; i <= _size; i++)
            {
                _str[i - len] = _str[i];
            }
            _size -= len;
        }
    }

    // 返回c在string中第一次出现的位置
    size_t string::find(char c, size_t pos) const
    {
        for (size_t i = pos; i < _size; i++)
        {
            if (_str[i] == c)
            {
                return i;
            }
        }
        return npos;
    }

    // 返回子串s在string中第一次出现的位置
    size_t string::find(const char* s, size_t pos) const
    {
        //可以直接使用C语言的库函数strstr()
        const char* str = strstr(_str + pos, s);
        //如果未找到相应的子串,就会返回 nullptr
        if (str == nullptr)
        {
            return npos;
        }
        else
        {
            return str - _str;
        }
    }

    string string::substr(size_t pos, size_t len)
    {
        if (len >= _size - pos)
        {
            len = _size - pos;
        }

        string sub;
        //预先开好空间,减少扩容
        sub.reserve(len);

        for (size_t i = 0; i < len; i++)
        {
            sub += _str[pos + i];
        }
        return sub;
    }

    bool operator<(const string& s1, const string& s2)
    {
        return strcmp(s1.c_str(), s2.c_str()) < 0;
    }

    bool operator<=(const string& s1, const string& s2)
    {
        return s1 < s2 || s1 == s2;
    }

    bool operator>(const string& s1, const string& s2)
    {
        return !(s1 <= s2);
    }

    bool operator>=(const string& s1, const string& s2)
    {
        return !(s1 < s2);
    }

    bool operator==(const string& s1, const string& s2)
    {
        return strcmp(s1.c_str(), s2.c_str()) == 0;
    }

    bool operator!=(const string& s1, const string& s2)
    {
        return !(s1 == s2);
    }

    ostream& operator<<(ostream& out, const string& s)
    {
        for (auto ch : s)
        {
            out << ch;
        }

        return out;
    }

    istream& operator>>(istream& in, string& s)
    {
        //重新输入数据之前要先把之前的数据清空
        s.clear();

        char ch;
        //in >> ch;
        ch = in.get();

        while (ch != ' ' && ch != '\n')
        {
            s += ch;
            //in >> ch;
            ch = in.get();
        }

        return in;
    }
}

StringTest.c

#include "String.h"

namespace Friend
{
    void StringTest1()
    {
        string s;
        cout << s << endl;
        string s1("hello,world");
        cout << s1 << endl;
        s.push_back('x');
        cout << s << endl;
        s.append("*****");
        cout << s << endl;
        s += "OOOO";
        cout << s << endl;

        s1.insert(0, 'x');
        cout << s1 << endl;

        s1.insert(4, "MM");
        cout << s1 << endl;

        s1.erase(0, 3);
        cout << s1 << endl;

        s1.erase(6);
        cout << s1 << endl;

        size_t find1 = s1.find('M', 0);
        cout << find1 << endl;

        size_t find2 = s.find("OOO", 0);
        cout << find2 << endl;

        string suffix = s.substr(find2, 4);
        cout << suffix << endl;

        cout << s << endl;
        cin >> s;
        cout << s << endl;
    }
}

int main()
{
    Friend::StringTest1();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2161372.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IO相关流

IO流 一、C语言的输入与输出1、介绍2、输入输出缓冲区&#xff08;1&#xff09;介绍&#xff08;2&#xff09;示意图 二、流1、介绍2、主要特点 三、CIO流1、介绍2、示意图 四、iostream1、介绍2、基本概念3、注意 五、类型转换1、operator bool&#xff08;1&#xff09;介绍…

计算机毕业设计非遗项目网站 登录注册搜索 评论留言资讯 前后台管理/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序

遗项目网站需求&#xff0c;以下是一个基于Spring Boot、Java Web、J2EE技术栈&#xff0c;使用MySQL数据库&#xff0c;并结合Vue实现前后端分离的简要设计方案&#xff1a; 系统功能概述 ‌用户登录与注册‌&#xff1a;实现用户的注册、登录功能&#xff0c;确保用户信息的…

【Python】PyCharm: 强大的 Python 开发环境

⭕️宇宙起点 &#x1f4e2; 引言&#x1f3ac; 什么是 PyCharm&#xff1f;&#x1f528; PyCharm 的核心特性1. 智能代码编辑2. 调试和测试3. 项目和代码结构导航4. 集成 AI 助手5. 远程开发6. 集成数据库7. 科学工具8. 版本控制集成9. Web 开发 &#x1f4e6; 安装 PyCharm&…

【NLP】daydayup 词向量训练模型word2vec

词嵌入算法 word2vec word2vec是一种高效训练词向量的模型&#xff0c;基本出发点是上下文相似的两个词。它们的词向量也应该相似。一般分为CBOW&#xff08;Continuous Bag-of-Words&#xff09;与 Skip-Gram CBOW 词袋模型&#xff0c;使用中心词周围的词来预测中心词&…

《微信小程序实战(4) · 地图导航功能》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

seL4 Mapping(三)

官网链接: Mapping Mapping 这节课程主要是介绍seL4的虚存管理。 虚存 Virtual memory 除了用于操作硬件分页结构的内核原语之外&#xff0c;seL4不提供虚拟内存管理。用户必须为创建中间级分页结构&#xff0c;映射页面以及取消映射页面提供服务。 用户可以随意的定义他们…

Python图表显示添加中文

import re import numpy as np import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties# 动态加载字体文件 font_path /usr/local/sunlogin/res/font/wqy-zenhei.ttc # 替换为实际字体路径 my_font FontProperties(fnamefont_path)# 定义日志…

Go语言基础学习01-Liunx下Go开发环境配置;源码组织方式;go build/install/get详解

目录 Linux环境下配置安装VScode并配置Go语言开发环境Go语言源码的组织方式Go语言源码安装后的结果Go程序构建和安装的过程go build扩展go get 命令详解 之前学习过Go语言&#xff0c;学习的时候没有记录笔记&#xff0c;最近找了个极客时间的Go语言36讲&#xff0c;打算时间学…

影响RPA流程稳定运行的若干因素|实在RPA研究

RPA发展现状 当前&#xff0c;中国正处于实现高质量发展、数字化转型升级的关键时期。RPA作为数字化转型的一项重要工具&#xff0c;已经开始在许多领域发挥积极作用。 RPA&#xff08;Robotic Process Automation 机器人流程自动化&#xff09;是一种通过软件机器人自动执行…

stm32 keil有一些别人的工程在你这打开为什么会乱码?如何解决的

因为别人编辑代码使用的编辑器和你的不一样&#xff0c;要更正可以调一下自己的翻译器编码格式 也可以直接换掉文件的格式&#xff0c; 用记事本打开文件&#xff0c;然后点会另存为&#xff0c;下面有个编码格式选择&#xff0c;换成你自己的就行

Neko一个在Docker环境下的虚拟浏览器

Neko是一个在 Docker 中运行并使用 WebRTC 技术的自托管虚拟浏览器。Neko 是一个强大的工具&#xff0c;可让您在虚拟环境中运行功能齐全的浏览器&#xff0c;使您能够从任何地方安全、私密地访问互联网。使用 Neko&#xff0c;您可以像在常规浏览器上一样浏览 Web、运行应用程…

Python接口测试实践—参数化测试、数据驱动测试和断言的使用

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在Python接口测试实践中&#xff0c;参数化测试、数据驱动测试和断言是常用的技术手段。 参数化测试 参数化测试是指将测试用例中的某些部分&#xff08;如输入数…

蓝桥杯算法之暴力

暴力 1.十进制数转换成罗马数字 2.判断给出的罗马数字是否正确 小知识 %&#xff08;模除&#xff09;&#xff1a; % 符号用作模除&#xff08;或取模&#xff09;运算符。模除运算是一种数学运算&#xff0c;它返回两个数相除的余数。 具体来说&#xff0c;如果 a 和 b 是…

初识 C++ ( 1 )

引言&#xff1a;大家都说c是c的升级语言。我不懂这句话的含义后来看过解释才懂。 一、面向过程语言和面向对象语言 我们都知道C语言是面向过程语言&#xff0c;而C是面向对象语言&#xff0c;说C和C的区别&#xff0c;也就是在比较面向过程和面向对象的区别。 1.面向过程和面向…

自然语言处理实战项目:从理论到实现

一、引言 自然语言处理&#xff08;NLP&#xff09;是计算机科学、人工智能和语言学交叉的领域&#xff0c;旨在让计算机能够理解、处理和生成人类语言。随着互联网的飞速发展&#xff0c;大量的文本数据被产生&#xff0c;这为自然语言处理技术的发展提供了丰富的素材&#xf…

【动态规划】(五)动态规划——子序列问题

动态规划——子序列问题 子序列问题☆ 最长递增子序列&#xff08;离散&#xff09;最长连续递增序列&#xff08;连续&#xff09;最大子序和&#xff08;连续&#xff09;最长重复子数组&#xff08;连续&#xff09;☆ 最长公共子序列&#xff08;离散-编辑距离过渡&#xf…

【驱动】修改USB转串口设备的属性,如:Serial

1、查看串口信息 在Windows上,设备管理窗口中查看设备号 2、修改串口号工具 例如使用:CH34xSerCfg.exe 使用步骤:恢复默认值 - -> 修改 Serial String(或者Product String等属性)–> 写入配置 3、查看设备节点 在linux上使用lsub查看新增的设备信息,如下这个…

python多线程开发的具体示例

用一个具体的示例&#xff0c;展示如何使用 ThreadPoolExecutor 和 asyncio 来并行运行多个任务&#xff0c;并输出结果。 代码&#xff1a; import asyncio import time from concurrent.futures import ThreadPoolExecutorclass WorkJob:def __init__(self, job_id):self.j…

报表做着太费劲?为你介绍四款好用的免费报表工具

1. 山海鲸可视化 介绍&#xff1a; 山海鲸可视化是一款免费的国产可视化报表软件&#xff0c;与许多其他宣传免费的软件不同&#xff0c;山海鲸的报表功能完全免费并且没有任何限制&#xff0c;就连网站管理后台这个功能也是免费的。同时山海鲸可视化还提供了种类丰富的可视化…

11.安卓逆向-安卓开发基础-api服务接口设计2

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a;图灵Python学院 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要盲目相信。 工…