初识c++(string和模拟实现string)

news2024/11/22 15:55:27

一、标准库中的string类

string类的文档介绍:cplusplus.com/reference/string/string/?kw=string

1、auto和范围for

auto

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个

不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型

指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期

推导而得。

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际

只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

auto不能作为函数的参数,可以做返回值,但是建议谨慎使用

auto不能直接用来声明数组

#include<iostream>
using namespace std;
int func1()
{
	return 10;
}
// 不能做参数
void func2(auto a)
{}
// 可以做返回值,但是建议谨慎使用
auto func3()
{
	return 3;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = func1();
	// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
	auto e;
	cout << typeid(b).name() << endl;//查看变量类型
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	int x = 10;
	auto y = &x;
	auto* z = &x;
	auto& m = x;
	cout << typeid(x).name() << endl;
	cout << typeid(y).name() << endl;
	cout << typeid(z).name() << endl;
	auto aa = 1, bb = 2;
	// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
	auto cc = 3, dd = 4.0;
	// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
	auto array[] = { 4, 5, 6 };
	return 0;
}

范围for

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此

C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围

内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。

范围for可以作用到数组和容器对象上进行遍历

范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。

#include<iostream>
#include <string>
#include <map>
using namespace std;
int main()
{
	int array[] = { 1, 2, 3, 4, 5 };
	// C++98的遍历
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
	{
		array[i] *= 2;
	}
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
	{
		cout << array[i] << endl;
	}
	// C++11的遍历
	for (auto& e : array)
		e *= 2;
	for (auto e : array)
		cout << e << " " << endl;
	string str("hello world");
	for (auto ch : str)
	{
		cout << ch << " ";
	}
	cout << endl;
	return 0;
}

2、string常见接口

1、 string类对象的常见构造

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

void Teststring()
{
	string s1; // 构造空的string类对象s1
	string s2("hello bit"); // 用C格式字符串构造string类对象s2
	string s3(s2); // 拷贝构造s3
}

2、string类对象的容量操作

在这里插入图片描述

注意:

  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接

口保持一致,一般情况下基本都是用size()。

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

  2. 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不会改变容量大小。

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

在这里插入图片描述

4、string类对象的修改操作

在这里插入图片描述

注意:

  1. 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差

不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可

以连接字符串。

  1. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留

好。

5、string类非成员函数

在这里插入图片描述

6、vs和g++下string结构的说明

注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节。

vs下string的结构

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++下string的结构**

G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个

指针,该指针将来指向一块堆空间,内部包含了如下字段:

空间总大小

字符串有效长度

引用计数

struct _Rep_base
{
	size_type _M_length;
	size_type _M_capacity;
	_Atomic_word _M_refcount;
};

二、string类的模拟实现

1、构造和析构

对于构造和析构这种频繁调用的函数可以直接在类里面写设为inline

构造:

string(const char* str = "")//空拷贝时就一个\0套到下面可以实现。
{
    _size = strlen(str);//
    // _capacity计算大小不包含\0
    _capacity = _size;
    _str = new char[_capacity + 1];
    strcpy(_str, str);//把原来的数据拷贝过来。
}

拷贝构造:(拷贝构造一定要深拷贝)

string(const string& s)//拷贝构造
{
    _size = s._size;
    _capacity = _capacity;
    _str = new char[_capacity + 1];
    strcpy(_str, s._str);//把原来的数据拷贝过来。
}

析构

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

赋值

 string& operator=(const string& s)
 {
     if (this != &s)//两个是不同的
     {
         delete[] _str;

         _str = new char[s._capacity + 1];//开的时候比capacity多开一个保证两个空间相同。
         strcpy(_str, s._str);
         _size = s._size;
         _capacity = s._capacity;
     }
     return *this;//两个相同返回本身。
 }

深浅拷贝问题:

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致

多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该

资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

在这里插入图片描述

深拷贝如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。

一般情况都是按照深拷贝方式提供。

在这里插入图片描述

2、迭代器

string的迭代器本质就是指针。

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

3、容量操作

size_t size()
{
    return _size;;
}
size_t capacity()const
{
    return _capacity;
}
bool empty()const
{
    return _size == 0;
}
void clear()
{
    *_str = '\0';//第一个为\0
    _size = 0;//长度为0
}
char& operator[](size_t index)
{
    assert(index < _size);
    return _str[index];
}
const char& operator[](size_t pos) const
{
    assert(pos < _size);

    return _str[pos];
}
void string::reserve(size_t n)
{
    if (n > _capacity)//大了才能改
    {
        auto* tmp = new char[n + 1];
        strcpy(tmp, _str);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
    else
    {
        perror("reserve fail");
    }
}
void string::resize(size_t n, char c)
{
    if (_size > n)
    {
        _str[n] = c;
        _size = n;
    }
    else
    {
        reserve(n);//扩空间
        for (size_t i = _size; i < n; i++)
        {
            _str[i] = c;
        }
        _size = n;
    }
}

4、修改操作

void string::push_back(char c)
{
    if (_size == _capacity)
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    _str[_size] = c;
    ++_size;
    _str[_size] = '\0';
}
void string::append(const char* str)
{
    size_t len = strlen(str);
    if (_size + len > _capacity)
    {
        // 大于2倍,需要多少开多少,小于2倍按2倍扩
        reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
    }
    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;
}
size_t string::find(char c, size_t pos) const
{
    assert(pos < _size);
    for (size_t i = pos; i < _size; i++)
    {
        if (_str[i] == c) 
        {
            return i;
        }
    }
    return npos;
}
size_t string::find(const char* str, size_t pos) const
{
    assert(pos < _size);
    const char* ptr = strstr(_str + pos, str);//直接用找字串的函数
    if (ptr == nullptr)
    {
        return npos;
    }
    else
    {
        return ptr - _str;
    }
}
string& string::insert(size_t pos, char c)
{
    assert(pos <= _size);
    if (_size == _capacity)
    {
        reserve(_capacity == 0 ? 4 : _capacity * 2);
    }
    for (size_t i = _size + 1; i > pos; i--)
    {
        _str[i] = _str[i - 1];
    }
    _str[pos] = c;
    ++_size;
    return *this;
}
string& string::insert(size_t pos, const char* str)
{
    assert(pos <= _size);
    if (_size + strlen(str) > _capacity)
    {
        // 大于2倍,需要多少开多少,小于2倍按2倍扩
        reserve(_size + strlen(str) > 2 * _capacity ? _size + strlen(str) : 2 * _capacity);
    }
    for (size_t i = _size + 1 + strlen(str); i > pos ; i--)
    {
        _str[i] = _str[i - strlen(str)];
    }
    for (size_t i = 0; i < strlen(str); i++)//用strcpy有数据丢失风险
    {
        _str[pos + i] = str[i];
    }
    _size += strlen(str);
    return *this;
}
string& string::erase(size_t pos, size_t len)
{
    assert(pos < _size);

    if (len >= _size - pos)//大于就全删
    {
        _str[pos] = '\0';
        _size = pos;
    }
    else
    {
        for (size_t i = pos + len; i <= _size; i++)//把len长度删了
        {
            _str[i - len] = _str[i];
        }
        _size -= len;
    }
}

5、比较操作

比较操作放在类外面比较好用

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);
}

6、cout和cin

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

        return _cout;
    }
    istream& operator>>(istream& in, string& s)
    {
        s.clear();//每次读之前清理一下
        const int N = 256;
        char buff[N];//定义一个buff数组避免频繁扩容
        int i = 0;
        char ch;
        //in >> ch;//不用>>是因为它默认以空格和换行为分割符。
        ch = in.get();
        while (ch != ' ' && ch != '\n')
        {
            buff[i++] = ch;
            if (i == N - 1)
            {
                buff[i] = '\0';
                s += buff;//满了就加给s
                i = 0;
            }
            ch = in.get();
        }
        if (i > 0)
        {
            buff[i] = '\0';//出来如果每满加\0并给s
            s += buff;
        }
        return in;
    }
}

7、所有文件

string.h

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
#include<string>
#include<assert.h>
using namespace std;
namespace str
{
    class string
    {
    public:
        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;
        }
    public:
        string(const char* str = "")//空拷贝时就一个\0套到下面可以实现。
        {
            _size = strlen(str);
            // _capacity计算大小不包含\0
            _capacity = _size;
            _str = new char[_capacity + 1];//多开一个放\0
            strcpy(_str, str);//把原来的数据拷贝过来。
        }
        string(const string& s)//拷贝构造
        {
            _size = s._size;
            _capacity = _capacity;
            _str = new char[_capacity + 1];
            strcpy(_str, s._str);//把原来的数据拷贝过来。
        }
        string& operator=(const string& s)
        {
            if (this != &s)//两个是不同的
            {
                delete[] _str;

                _str = new char[s._capacity + 1];//开的时候比capacity多开一个保证两个空间相同。
                strcpy(_str, s._str);
                _size = s._size;
                _capacity = s._capacity;
            }
            return *this;//两个相同返回本身。
        }
        ~string()
        {
            delete[] _str;
            _str = nullptr;
            _capacity = _size = 0;
        }
        void push_back(char c);
        string& operator+=(char c);
        void append(const char* str);
        string& operator+=(const char* str);
        const char* c_str() const;
        void clear()
        {
            *_str = '\0';//第一个为\0
            _size = 0;//长度为0
        }
        /
        // capacity
        size_t size()
        {
            return _size;;
        }
        size_t capacity()const
        {
            return _capacity;
        }
        bool empty()const
        {
            return _size == 0;
        }
        void resize(size_t n, char c = '\0');
        void reserve(size_t n);
        /
        // access
        char& operator[](size_t index)
        {
            assert(index < _size);
            return _str[index];
        }
        const char& operator[](size_t pos) const
        {
            assert(pos < _size);

            return _str[pos];
        }
        /
        //relational operators
        
        // 返回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;
        // 在pos位置上插入字符c/字符串str,并返回该字符的位置
        string& insert(size_t pos, char c);
        string& insert(size_t pos, const char* str);
        // 删除pos位置上的元素,并返回该元素的下一个位置
        string& erase(size_t pos, size_t len);
    private:
        char* _str;
        size_t _capacity;
        size_t _size;
        static size_t nops;
    };
    ostream& operator<<(ostream& _cout, const str::string& s);
    istream& operator>>(istream& _cin, str::string& s);
    static size_t npos = -1;
    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);
}

string.c

#include"string.h"
#include<iostream>
using namespace std;
namespace str
{
    void string::reserve(size_t n)
    {
        if (n > _capacity)//大了才能改
        {
            auto* tmp = new char[n + 1];
            strcpy(tmp, _str);
            delete[] _str;
            _str = tmp;
            _capacity = n;
        }
        else
        {
            perror("reserve fail");
        }
    }
    void string::resize(size_t n, char c)
    {
        if (_size > n)
        {
            _str[n] = c;
            _size = n;
        }
        else
        {
            reserve(n);//扩空间
            for (size_t i = _size; i < n; i++)
            {
                _str[i] = c;
            }
            _size = n;
        }
    }
    void string::push_back(char c)
    {
        if (_size == _capacity)
        {
            reserve(_capacity == 0 ? 4 : _capacity * 2);
        }
        _str[_size] = c;
        ++_size;
        _str[_size] = '\0';
	}
    void string::append(const char* str)
    {
        size_t len = strlen(str);
        if (_size + len > _capacity)
        {
            // 大于2倍,需要多少开多少,小于2倍按2倍扩
            reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
        }
        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;
    }
    size_t string::find(char c, size_t pos) const
    {
        assert(pos < _size);
        for (size_t i = pos; i < _size; i++)
        {
            if (_str[i] == c) 
            {
                return i;
            }
        }
        return npos;
    }
    size_t string::find(const char* str, size_t pos) const
    {
        assert(pos < _size);
        const char* ptr = strstr(_str + pos, str);//直接用找字串的函数
        if (ptr == nullptr)
        {
            return npos;
        }
        else
        {
            return ptr - _str;
        }
    }
    string& string::insert(size_t pos, char c)
    {
        assert(pos <= _size);
        if (_size == _capacity)
        {
            reserve(_capacity == 0 ? 4 : _capacity * 2);
        }
        for (size_t i = _size + 1; i > pos; i--)
        {
            _str[i] = _str[i - 1];
        }
        _str[pos] = c;
        ++_size;
        return *this;
    }
    string& string::insert(size_t pos, const char* str)
    {
        assert(pos <= _size);
        if (_size + strlen(str) > _capacity)
        {
            // 大于2倍,需要多少开多少,小于2倍按2倍扩
            reserve(_size + strlen(str) > 2 * _capacity ? _size + strlen(str) : 2 * _capacity);
        }
        for (size_t i = _size + 1 + strlen(str); i > pos ; i--)
        {
            _str[i] = _str[i - strlen(str)];
        }
        for (size_t i = 0; i < strlen(str); i++)//用strcpy有数据丢失风险
        {
            _str[pos + i] = str[i];
        }
        _size += strlen(str);
        return *this;
    }
    string& string::erase(size_t pos, size_t len)
    {
        assert(pos < _size);

        if (len >= _size - pos)//大于就全删
        {
            _str[pos] = '\0';
            _size = pos;
        }
        else
        {
            for (size_t i = pos + len; i <= _size; i++)//把len长度删了
            {
                _str[i - len] = _str[i];
            }
            _size -= len;
        }
    }
    const char* string::c_str() const
    {
        return _str;
    }
    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& _cout, const str::string& s)
    {
        for (auto ch : s)
        {
            _cout << ch;
        }

        return _cout;
    }
    istream& operator>>(istream& in, string& s)
    {
        s.clear();//每次读之前清理一下
        const int N = 256;
        char buff[N];//定义一个buff数组避免频繁扩容
        int i = 0;
        char ch;
        //in >> ch;//不用>>是因为它默认以空格和换行为分割符。
        ch = in.get();
        while (ch != ' ' && ch != '\n')
        {
            buff[i++] = ch;
            if (i == N - 1)
            {
                buff[i] = '\0';
                s += buff;//满了就加给s
                i = 0;
            }
            ch = in.get();
        }
        if (i > 0)
        {
            buff[i] = '\0';//出来如果每满加\0并给s
            s += buff;
        }
        return in;
    }
}

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

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

相关文章

【北航主办丨本届SPIE独立出版丨已确认ISSN号】第三届智能机械与人机交互技术学术会议(IHCIT 2024,7月27)

由北京航空航天大学指导&#xff0c;北京航空航天大学自动化科学与电气工程学院主办&#xff0c;AEIC学术交流中心承办的第三届智能机械与人机交互技术学术会议&#xff08;IHCIT 2024&#xff09;将定于2024年7月27日于中国杭州召开。 大会面向基础与前沿、学科与产业&#xf…

初识c++:string类 (1)

目录 # 初识c&#xff1a;string类 1.为什么学习string类 2.标准库中的string类 2.1 string类的了解 2.2 auto和范围for 2.3 string类的常用接口说明 2.3.1string类对象的常见构造 2.3.2string类对象的容量操作 2.3.3string类对象的访问及遍历操作 2.3.4string类对象…

DNS概述及DNS服务器的搭建(twelve day)

回顾 关闭防火墙 systemctl stop firewalld 永久停止防火墙 systemctl disable firewalld 关闭selinux setenforce 0 永久关闭selinux安全架构 vim /etc/selinux/config 修改静态IP地址 vim /etc/sysconfig/network-scripts/ifcfg-ens160 #修改uuid的目的是为了保证网络的唯一…

计算机的错误计算(四十)

摘要 计算机的错误计算&#xff08;三十九&#xff09;阐明有时计算机将0算成非0&#xff0c;非0算成0&#xff1b;并且前面介绍的这些错误计算相对来说均是由软件完成。本节讨论手持式计算器对这些算式的计算效果。 例1. 用手持式计算器计算 与 . 我们用同一个计算器计算…

机械学习—零基础学习日志(高数10——函数图形)

零基础为了学人工智能&#xff0c;真的开始复习高数 函数图像&#xff0c;开始新的学习&#xff01;本次就多做一做题目&#xff01; 第一题&#xff1a; 这个解法是有点不太懂的了。以后再多研究一下。再出一道题目。 张宇老师&#xff0c;比较多提示了大家&#xff0c;一定…

哪些工作可以年入几十万到2亿?

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 从今年起&#xff0c; 每个月都会有年入几十万到2亿的新闻案例出来&#xff0c;而且很多都是官方媒体发的&#xff0c;你们看看&#xff1a; 7月19日35岁小伙扛楼一年多存了40万 7月4日老板娘一天卖出200斤知了日入…

Leetcode3217. 从链表中移除在数组中存在的节点

Every day a Leetcode 题目来源&#xff1a;3217. 从链表中移除在数组中存在的节点 解法1&#xff1a;集合 链表遍历 代码&#xff1a; /** lc appleetcode.cn id3217 langcpp** [3217] 从链表中移除在数组中存在的节点*/// lc codestart /*** Definition for singly-link…

docker--容器数据进行持久化存储的三种方式

文章目录 为什么Docker容器需要使用持久化存储1.什么是Docker容器&#xff1f;2.什么是持久化存储&#xff1f;3.为什么Docker容器需要持久化存储&#xff1f;4.Docker如何实现持久化存储&#xff1f;(1)、Docker卷(Volumes)简介适用环境:使用场景:使用案例: (2)、绑定挂载&…

Python 实现PDF和TIFF图像之间的相互转换

PDF是数据文档管理领域常用格式之一&#xff0c;主要用于存储和共享包含文本、图像、表格、链接等的复杂文档。而TIFF&#xff08;Tagged Image File Format&#xff09;常见于图像处理领域&#xff0c;主要用于高质量的图像文件存储。 在实际应用中&#xff0c;我们可能有时需…

哪个邮箱最安全最好用啊

企业邮箱安全至关重要&#xff0c;需保护隐私、防财务损失、维护通信安全、避免纠纷&#xff0c;并维持业务连续性。哪个企业邮箱最安全好用呢&#xff1f;Zoho企业邮箱&#xff0c;采用加密技术、反垃圾邮件和病毒保护&#xff0c;支持多因素认证&#xff0c;确保数据安全合规…

php的文件上传

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…

【每日刷题Day85】

【每日刷题Day85】 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 125. 验证回文串 - 力扣&#xff08;LeetCode&#xff09; 2. 43. 字符串相乘 - 力扣&#xff08;L…

【es】elasticsearch 自定义排序-按关键字位置排序

一 背景 要求es查询的结果按关键字位置排序&#xff0c;位置越靠前优先级越高。 es版本7.14.0&#xff0c;项目是thrift&#xff0c;也可以平替springboot&#xff0c;使用easyes连接es。 二 easyes使用 配easyes按官方文档就差不多了 排序 | Easy-Es 主要的一个问题是easy…

FPGA深入浅出IP核学习(一)-- vivado中clk IP MMCM核的使用

FPGA深入浅出IP核学习系列文章主要是自己关于学习FGPA IP核的学习笔记&#xff0c;供大家参考学习指正。 目录 前言 一、MMCM-IP核简介 二、MMCM-IP核使用 1.IP核配置 2.模块程序设计 总结 前言 本文主要参考B站野火FGPA相关学习视频、正点原子达芬奇FPGA开发指南和赛灵思官方用…

一文入门SpringSecurity 5

目录 提示 Apache Shiro和Spring Security 认证和授权 RBAC Demo 环境 Controller 引入Spring Security 初探Security原理 认证授权图示​编辑 图中涉及的类和接口 流程总结 提示 Spring Security源码的接口名和方法名都很长&#xff0c;看源码的时候要见名知意&am…

docker笔记4-镜像理解

docker笔记4-镜像理解 一、镜像原理之联合文件系统二、镜像原理之分层理解三、commit镜像 一、镜像原理之联合文件系统 UnionFS&#xff08;联合文件系统&#xff09;: Union文件系统&#xff08;UnionFS&#xff09;是一种分层、轻量级并且高性能的文件系统&#xff0c;它支持…

深入了解路由器工作原理:从零开始的简单讲解

简介 在现代网络中&#xff0c;路由器扮演着至关重要的角色。它不仅连接了不同的设备&#xff0c;还确保数据能够准确地传输到目的地。本文将带你深入探讨路由器的工作原理&#xff0c;帮助网络基础小白们理解这一重要设备的基本功能。 路由器的构成 路由器是一种具有多个输入…

云计算实训12——配置web服务器、配置客户端服务器、配置DNS服务、实现DNS域名解析

一、配置web服务器 准备操作 首先在正式配置之前需要做以下操作 关闭防火墙 systemctl stop firewalld 永久关闭防火墙 systemctl disable firewalld 关闭selinux setenforce 0 永久关闭selinux vim /etc/selinux/config selinuxpermissive 还需要保证能够正常ping通www.bai…

使用uniapp开发小程序(基础篇)

本文章只介绍微信小程序的开发流程&#xff0c;如果需要了解其他平台的开发的流程的话&#xff0c;后续根据情况更新相应的文章,也可以根据uniapp官网的链接了解不同平台的开发流程 HBuilderX使用&#xff1a;https://uniapp.dcloud.net.cn/quickstart-hx.html 开发工具 开始…

【Django】在vscode中运行调试Django项目(命令及图形方式)

文章目录 命令方式图形方式默认8000端口设置自定义端口 命令方式 python manage.py runserver图形方式 默认8000端口 设置自定义端口