string类函数的手动实现
在上一篇文章中,我们讲解了一些string类的函数,但是对于我们要熟练掌握c++是远远不够的,今天,我将手动实现一下这些函数~
注意:本篇文章中会大量应用复用,这是一种很巧妙的方法
和以往一样,还是分为string.h string.cpp test.cpp三个文件
为了保证完整性,string.h我统一放在这
1.string.h文件
#pragma once
#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;
namespace my_string
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}string(const char* str = "");string(size_t n, char ch);string(const string& s);string& operator=(const string& s);~string();void clear(){_str[0] = '\0';_size = 0;}const char* c_str() const{return _str;}void reserve(size_t n);void push_back(char ch);void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);void insert(size_t pos, size_t n, char ch);void insert(size_t pos, const char* str);void erase(size_t pos = 0, size_t len = npos);size_t find(char ch, size_t pos = 0);size_t find(const char* str, size_t pos = 0);size_t size()const{return _size;}size_t capacity()const{return _size;}char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}string substr(size_t pos, size_t len = npos);bool operator==(const string& s)const;bool operator!=(const string& s)const;bool operator<(const string& s)const;bool operator<= (const string & s)const;bool operator>(const string& s)const;bool operator>=(const string& s)const;private:char* _str;size_t _size;size_t _capacity;const static size_t npos;};//cout<<s1ostream& operator<<(ostream& out, const string& s);//cin>>s1istream& operator<<(istream& in, string& s);istream& getline(istream& is, string& s, char delim = '#');}
2. 增加类函数(append\insert\push_back\+=)
这是string.cpp文件
void string::push_back(char ch){if (_size + 1 > _capacity){//意味着此时已经满了,需要扩容才能插入//扩容,建议使用函数复用//还要讨论原来容量是不是0reserve(_capacity == 0 ? 4 : _capacity * 2);}//此时已经完成扩容,容量足够用_str[_size] = ch;_size++;_str[_size] = '\0'; //别忘了把\0也考过来}void string::append(const char* str){//注:我们这里是直接按照库里的思路去实现的 // 在这里扩容_size+len也是可以的 只不过思路不一样// 也可能官方认为追加直接扩二倍 后面人继续使用的时候可以少调几次开空间吧size_t len = strlen(str);if (_size + len > _capacity){//意味着此时已经满了,需要扩容才能插入//扩容,与push_back不同的是,// 默认使用append是认为你这个字符串原先就是有内容才追加的// 如果害怕有人确实会直接使用这个接口 可以加上_capacity=0的情况size_t newcapacity = 2 * _capacity;//考虑到可能插入的字符串过长,2倍扩容都可能不够//为防止越界的产生,我们再严谨的讨论一下if (_size + len > 2 * _capacity){newcapacity = _size + len;}reserve(newcapacity);}//strcpy在拷贝时会从第一个字符出发找\0,// 为了节约编译器运行时间,我们直接手动让他从\0出发strcpy(_str + _size, str); _size += len; }void string::insert(size_t pos, size_t n, char ch){assert(pos <= _size);assert(n > 0);//还是要考虑扩容问题if (_size + n > _capacity){size_t newcapacity = 2 * _capacity;if (_size + n > 2 * _capacity){newcapacity = _size + n;}reserve(newcapacity);}size_t end = _size + n; //一切以\0为准while (end > pos + n - 1) //准备挪动数据,这是需要挪动的数据范围{_str[end] = _str[end - n];//最后一个数据先动end--;}//挪完了,有地方了,但是要插入的数还没进来呢!for (size_t i = 0; i < n; i++){_str[pos + i] = ch;}_size += n;}void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t n = strlen(str);if (_size + n > _capacity){size_t newCapacity = 2 * _capacity;if (_size + n > 2 * _capacity){newCapacity = _size + n;}reserve(newCapacity);}size_t end = _size + n;while (end > pos + n - 1){_str[end] = _str[end - n];--end;}for (size_t i = 0; i < n; i++){_str[pos + i] = str[i];}}
string& string::operator+=(char ch)
{push_back(ch);return *this;
}
string& string::operator+=(const char* str)
{append(str);return *this;
}
这是test.cpp文件
#include"string.h"
void test_string1()
{string s1("hello world");cout << s1.c_str() << endl;s1 += ' ';cout << s1.c_str() << endl;s1 += '+';cout << s1.c_str() << endl;s1 += "hello everybody";cout << s1.c_str() << endl;s1.push_back(',');cout << s1.c_str() << endl;s1.append("welcome!");cout << s1.c_str() << endl;s1.insert(6,1, 't');cout << s1.c_str() << endl;s1.insert(7, "he ");cout << s1.c_str() << endl;s1.insert(41, "nice to meet you");cout << s1.c_str() << endl;s1.insert(0, "good morning!");cout << s1.c_str() << endl;
}
int main()
{test_string1();return 0;
}
结果如下:
2.find和erase
这是string.cpp文件
size_t string::find(char ch, size_t pos){for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}size_t string::find(const char* str, size_t pos){const char* p = strstr(_str + pos, str); //strstr() 函数的作用是在一个字符串(str1)中查找另一个字符串(str2)的出现位置。//如果找到,它返回一个指向 str1 中第一次出现的 str2 的指针;// 如果找不到,则返回空指针(NULL)。if (p == nullptr){return npos;}else{return p - _str; //两个指针相减,结果得到这个元素的下标}}void string::erase(size_t pos, size_t len){if (len > _size - pos){_str[pos] = '\0';_size = pos;}else{size_t end = pos + len;{while (end <= _size){_str[end - len] = _str[end];++end;}_size -= len;}}}
这是test.cpp文件
void test_string_find_erase()
{string s1("hello world");cout << s1.c_str() << endl;s1.erase(6,2);cout << s1.c_str() << endl;s1.erase(6, 20);cout << s1.c_str() << endl;s1.erase(3);cout << s1.c_str() << endl;string s2("welcome to guangzhou!");cout << s2.find('o') << endl;cout << s2.find("guangzh") << endl;}
int main()
{test_string_find_erase();return 0;
}
结果如下:
3.迭代器
这是test.cpp文件
void test_string_iterator()
{string s1("hello world");for (size_t i = 0; i < s1.size(); i++){s1[i]++;cout << s1[i] << " ";}cout << endl;string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;for (auto e : s1){cout << e;}cout << endl;}
int main()
{test_string_iterator();return 0;
}
运行结果:
4.substr()
string string::substr(size_t pos, size_t len)
{size_t leftlen = _size - pos; //求出要截取部分长度if (len > leftlen){len = leftlen;}string tmp;tmp.reserve(len);for (size_t i = 0; i < len; i++){tmp += _str[pos + i];}return tmp;
}
void test_string5()
{string s1("hello world");string sub1 = s1.substr(6, 3);cout << sub1.c_str() << endl;string sub2 = s1.substr(6, 300);cout << sub2.c_str() << endl;string sub3 = s1.substr(6);cout << sub3.c_str() << endl;string s2("hello bitxxxxxxxxxxxxxxxxxx");s1 = s2;cout << s1.c_str() << endl;cout << s2.c_str() << endl;s1 = s1;cout << s1.c_str() << endl;
}
int main()
{//test_string_add();//test_string_find_erase();//test_string_iterator();test_string5();return 0;
}
5.流插入和提取
ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear(); //此举是为了防止s原有内容对输入的影响//类比我们要接满一个大水桶,但是我们不知道需要到底具体有多少水// 正好手里有一个可以装N升水的小盆,我们可以用这个小盆装水,满了后导入大桶里// 这样可以使得:// 输入短串,不会浪费空间// 输入长串,避免不断扩容const size_t N = 1024;char buff[N];int i = 0;char ch = in.get(); //获取首个单个字符while (ch != ' ' && ch != '\n');{buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get(); //获取其余诸多单个字符}//此时有两种情况:1是输入字符串的字符个数正好为N的整数倍,此时i==0;(盆里面没有水了)//2是输入字符串的字符个数不为N的整数倍,此时i>0;(盆里面还有水)if (i > 0){buff[i] = '\0';s += buff;}return in;}istream& getline(istream& in, string& s, char delim){s.clear();const size_t N = 1024;char buff[N];int i = 0;char ch = in.get();while (ch != delim){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}
void test_string6()
{string s1, s2;cin >> s1 >> s2;cout << s1 << endl;cout << s2 << endl;string s3;//getline(cin, s3);getline(cin, s3, '!');cout << s3 << endl;
}
int main()
{//test_string_add();//test_string_find_erase();//test_string_iterator();//test_string5();test_string6();return 0;
}
6.汇总:
这是string.h文件
#pragma once
#include<iostream>
#include<assert.h>
#include<string.h>
using namespace std;
namespace my_string
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}string(const char* str = "");string(size_t n, char ch);string(const string& s);string& operator=(const string& s);~string();void clear(){_str[0] = '\0';_size = 0;}const char* c_str() const{return _str;}void reserve(size_t n);void push_back(char ch);void append(const char* str);string& operator+=(char ch);string& operator+=(const char* str);void insert(size_t pos, size_t n, char ch);void insert(size_t pos, const char* str);void erase(size_t pos = 0, size_t len = npos);size_t find(char ch, size_t pos = 0);size_t find(const char* str, size_t pos = 0);size_t size()const{return _size;}size_t capacity()const{return _size;}char& operator[](size_t pos) const{assert(pos < _size);return _str[pos];}string substr(size_t pos, size_t len = npos);bool operator==(const string& s)const;bool operator!=(const string& s)const;bool operator<(const string& s)const;bool operator<= (const string & s)const;bool operator>(const string& s)const;bool operator>=(const string& s)const;private:char* _str;size_t _size;size_t _capacity;const static size_t npos;};//cout<<s1ostream& operator<<(ostream& out, const string& s);//cin>>s1istream& operator<<(istream& in, string& s);istream& getline(istream& is, string& s, char delim = '#');}
这是string.cpp文件
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
namespace my_string
{const size_t string::npos = -1;string::string(size_t n, char ch):_str(new char[n + 1]), _size(n), _capacity(n){for (size_t i = 0; i < n; i++){_str[i] = ch;}_str[_size] = '\0';}string::string(const char* str):_size(strlen(str)){_capacity = _size;_str = new char[_size + 1]; //多开一个空间放\0strcpy(_str, str);}//s2(s1)string::string(const string& s){_str = new char[s._capacity + 1]; //永远都记得多开一个strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}//s1=s2//s1=s1(不建议这样做)string& string::operator=(const string& s){//this :s1 s:s2if (this != &s) //避免s1=s1这种事件发生{//这里由于我们之前在构造_str的时候使用new[]了,但为了我们之后将s2拷贝给s1,//我们要开一个能装下s2的空间,所以这里我们先delete[],再new[]一个,用于拷贝s2,//注意,strcpy不能变插边扩容,这才是我们这么做的根本原因delete[] _str;_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}return *this; //我们要通过s2构造s1,故返回*this}string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}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 ch){if (_size + 1 > _capacity){//意味着此时已经满了,需要扩容才能插入//扩容,建议使用函数复用//还要讨论原来容量是不是0reserve(_capacity == 0 ? 4 : _capacity * 2);}//此时已经完成扩容,容量足够用_str[_size] = ch;_size++;_str[_size] = '\0'; //别忘了把\0也考过来}void string::append(const char* str){//注:我们这里是直接按照库里的思路去实现的 // 在这里扩容_size+len也是可以的 只不过思路不一样// 也可能官方认为追加直接扩二倍 后面人继续使用的时候可以少调几次开空间吧size_t len = strlen(str);if (_size + len > _capacity){//意味着此时已经满了,需要扩容才能插入//扩容,与push_back不同的是,// 默认使用append是认为你这个字符串原先就是有内容才追加的// 如果害怕有人确实会直接使用这个接口 可以加上_capacity=0的情况size_t newcapacity = 2 * _capacity;//考虑到可能插入的字符串过长,2倍扩容都可能不够//为防止越界的产生,我们再严谨的讨论一下if (_size + len > 2 * _capacity){newcapacity = _size + len;}reserve(newcapacity);}//strcpy在拷贝时会从第一个字符出发找\0,// 为了节约编译器运行时间,我们直接手动让他从\0出发strcpy(_str + _size, str); _size += len; }string& string::operator+=(char ch){push_back(ch);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::insert(size_t pos, size_t n, char ch){assert(pos <= _size);assert(n > 0);//还是要考虑扩容问题if (_size + n > _capacity){size_t newcapacity = 2 * _capacity;if (_size + n > 2 * _capacity){newcapacity = _size + n;}reserve(newcapacity);}size_t end = _size + n; //一切以\0为准while (end > pos + n - 1) //准备挪动数据,这是需要挪动的数据范围{_str[end] = _str[end - n];//最后一个数据先动end--;}//挪完了,有地方了,但是要插入的数还没进来呢!for (size_t i = 0; i < n; i++){_str[pos + i] = ch;}_size += n;}void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t n = strlen(str);if (_size + n > _capacity){size_t newCapacity = 2 * _capacity;if (_size + n > 2 * _capacity){newCapacity = _size + n;}reserve(newCapacity);}size_t end = _size + n;while (end > pos + n - 1){_str[end] = _str[end - n];--end;}for (size_t i = 0; i < n; i++){_str[pos + i] = str[i];}}void string::erase(size_t pos, size_t len){if (len > _size - pos){_str[pos] = '\0';_size = pos;}else{size_t end = pos + len;{while (end <= _size){_str[end - len] = _str[end];++end;}_size -= len;}}}size_t string::find(char ch, size_t pos){for (size_t i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}size_t string::find(const char* str, size_t pos){const char* p = strstr(_str + pos, str); //strstr() 函数的作用是在一个字符串(str1)中查找另一个字符串(str2)的出现位置。//如果找到,它返回一个指向 str1 中第一次出现的 str2 的指针;// 如果找不到,则返回空指针(NULL)。if (p == nullptr){return npos;}else{return p - _str; //两个指针相减,结果得到这个元素的下标}}string string::substr(size_t pos, size_t len){size_t leftlen = _size - pos; //求出要截取部分长度if (len > leftlen){len = leftlen;}string tmp;tmp.reserve(len);for (size_t i = 0; i < len; i++){tmp += _str[pos + i];}return tmp;}bool string::operator==(const string& s)const{return strcmp(_str, s._str) == 0;}bool string::operator!=(const string& s)const{return !(*this == s);}bool string::operator<(const string& s)const{return strcmp(_str, s._str) < 0;}bool string::operator<=(const string& s)const{return *this < s || *this == s;}bool string::operator>(const string& s)const{return !(*this <= s);}bool string::operator>=(const string& s)const{return *this == s || *this > s;}ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){s.clear(); //此举是为了防止s原有内容对输入的影响//类比我们要接满一个大水桶,但是我们不知道需要到底具体有多少水// 正好手里有一个可以装N升水的小盆,我们可以用这个小盆装水,满了后导入大桶里// 这样可以使得:// 输入短串,不会浪费空间// 输入长串,避免不断扩容const size_t N = 1024;char buff[N];int i = 0;char ch = in.get(); //获取首个单个字符while (ch != ' ' && ch != '\n');{buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get(); //获取其余诸多单个字符}//此时有两种情况:1是输入字符串的字符个数正好为N的整数倍,此时i==0;(盆里面没有水了)//2是输入字符串的字符个数不为N的整数倍,此时i>0;(盆里面还有水)if (i > 0){buff[i] = '\0';s += buff;}return in;}istream& getline(istream& in, string& s, char delim){s.clear();const size_t N = 1024;char buff[N];int i = 0;char ch = in.get();while (ch != delim){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';s += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';s += buff;}return in;}
}
这是test.cpp文件
#include"string.h"
#include<string>
void test_string_add()
{string s1("hello world");cout << s1.c_str() << endl;s1 += ' ';cout << s1.c_str() << endl;s1 += '+';cout << s1.c_str() << endl;s1 += "hello everybody";cout << s1.c_str() << endl;s1.push_back(',');cout << s1.c_str() << endl;s1.append("welcome!");cout << s1.c_str() << endl;s1.insert(6,1, 't');cout << s1.c_str() << endl;s1.insert(7, "he ");cout << s1.c_str() << endl;s1.insert(41, "nice to meet you");cout << s1.c_str() << endl;s1.insert(0, "good morning!");cout << s1.c_str() << endl;
}
void test_string_find_erase()
{string s1("hello world");cout << s1.c_str() << endl;s1.erase(6,2);cout << s1.c_str() << endl;s1.erase(6, 20);cout << s1.c_str() << endl;s1.erase(3);cout << s1.c_str() << endl;string s2("welcome to guangzhou!");cout << s2.find('o') << endl;cout << s2.find("guangzh") << endl;}
void test_string_iterator()
{string s1("hello world");for (size_t i = 0; i < s1.size(); i++){s1[i]++;cout << s1[i] << " ";}cout << endl;string::iterator it = s1.begin();while (it != s1.end()){cout << *it << " ";++it;}cout << endl;for (auto e : s1){cout << e;}cout << endl;}
void test_string5()
{string s1("hello world");string sub1 = s1.substr(6, 3);cout << sub1.c_str() << endl;string sub2 = s1.substr(6, 300);cout << sub2.c_str() << endl;string sub3 = s1.substr(6);cout << sub3.c_str() << endl;string s2("hello bitxxxxxxxxxxxxxxxxxx");s1 = s2;cout << s1.c_str() << endl;cout << s2.c_str() << endl;s1 = s1;cout << s1.c_str() << endl;
}
void test_string6()
{string s1, s2;cin >> s1 >> s2;cout << s1 << endl;cout << s2 << endl;string s3;//getline(cin, s3);getline(cin, s3, '!');cout << s3 << endl;
}
int main()
{//test_string_add();//test_string_find_erase();//test_string_iterator();//test_string5();test_string6();return 0;
}
相关文章:
string类函数的手动实现
在上一篇文章中,我们讲解了一些string类的函数,但是对于我们要熟练掌握c是远远不够的,今天,我将手动实现一下这些函数~ 注意:本篇文章中会大量应用复用,这是一种很巧妙的方法 和以往一样,还是…...
前端工程化面试题(二)
前端模块化标准 CJS、ESM 和 UMD 的区别 CJS(CommonJS)、ESM(ESModule)和UMD(Universal Module Definition)是前端模块化标准的三种主要形式,它们各自有不同的特点和使用场景: CJS&…...
优化 LabVIEW 系统内存使用
在 LabVIEW 中,内存使用管理是确保高效系统性能的关键因素,尤其是在进行复杂的数据采集、信号处理和控制任务时。LabVIEW 程序的内存消耗可能会随着项目的规模和复杂度增加,导致性能下降,甚至出现内存溢出或程序崩溃。通过合理优化…...
pyqt6事件概要
例子: 利用qtdesigner建立闹钟 python代码 # 导入所需要的文件 from PyQt6.QtGui import QIcon, QPixmap from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QListWidgetItem from PyQt6 import uic from PyQt6.QtCore import Qt, QTime imp…...
鸿蒙分享(一):添加模块,修改app名称图标
码仓库:https://gitee.com/linguanzhong/share_harmonyos 鸿蒙api:12 新建公共模块common 在entry的oh-package.json5添加dependencies,引入common模块 "dependencies": {"common": "file:../common" } 修改app名称&…...
记忆泡沫垫市场:解锁舒适睡眠的黄金钥匙与增长潜力的深度剖析
在当今快节奏、高压力的生活中,优质睡眠已成为现代人追求健康生活的重要组成部分。记忆泡沫垫,作为床垫和枕头领域的一次革命性创新,凭借其独特的材质特性和对人体工学的完美贴合,正逐步成为改善睡眠质量的首选解决方案。本文将从…...
AI+电影特效产品化:开启电影人物年轻化新时代
随着人工智能技术的不断进步,它正在改变着我们生活的方方面面,包括娱乐产业。在电影制作领域,AI技术的应用尤其引人注目,尤其是在实现演员年轻化或老化效果方面。本文将介绍一款名为MyTimeMach...
探索 Python 应用的分层依赖:解决 UOS 环境中的 libvirt-python 安装问题
探索 Python 应用的分层依赖:解决 UOS 环境中的 libvirt-python 安装问题 背景Python 版本升级 问题描述原因分析与解决方案 Python 应用的分层依赖:安装与部署的视角libvirt-python的分层依赖尝试的解决方案 使用编译好的 .whl 文件"嫁接"整个…...
【MySQL 进阶之路】表级锁、行级锁详解
1. 表级锁和行级锁的概念及区别 表级锁(Table Lock) 表锁是一种较为粗粒度的锁,锁定的是整个表。当某个事务加锁表时,其他事务对该表的任何读写操作都会被阻塞,直到锁被释放。因此,表锁具有较高的冲突概率…...
FPGA系列,文章目录
前言 FPGA(Field-Programmable Gate Array,现场可编程门阵列)是一种集成电路,其内部结构可以通过软件重新配置来实现不同的逻辑功能。与传统的ASIC(Application-Specific Integrated Circuit,专用集成电路…...
离谱的梯形滤波器——增加过渡点
增加过渡点 频率采样法(Frequency Sampling Method)是一种设计FIR滤波器的方法,通过在频域中指定希望的频率响应,然后利用逆离散傅里叶变换(IDFT)来获得滤波器的脉冲响应。然而,这种方法容易导…...
容积卡尔曼滤波(CKF)仿真抛物线运动
容积卡尔曼滤波(CKF)仿真抛物线运动 容积卡尔曼滤波(Cubature Kalman Filter, CKF)的MATLAB实现。CKF是一种用于非线性系统状态估计的算法,它通过在状态空间中采样点(容积点)来近似非线性函数的…...
FlightGear+MATLAB+飞行手柄实现实时飞控视景系统
文章目录 一、软件配置二、FlightGearMATLAB联合仿真第一步 复制文件第二步 新建文件夹第三步 打开demo第四步 demo说明第五步 打开Simulink第六步 连接FlightGear第七步 设置FlightGear第八步 生成FlightGear连接文件FlightGear的设置Network的设置File的设置生成.bat文件 第九…...
Oracle 11g Data Guard 环境中的 GAP 处理办法
概述 在Data Guard 环境中,当主库的某些日志没有成功传送到备库时,就会发生归档裂缝(Archive Gap)。目前,Oracle 提供了两种日志 GAP 的检测和处理机制:自动 GAP 处理(Automatic Gap Resolutio…...
自建服务器,数据安全有保障
在远程桌面工具的选择上,向日葵和TeamViewer功能强大,但都存在收费昂贵、依赖第三方服务器、数据隐私难以完全掌控等问题。相比之下,RustDesk 凭借开源免费、自建服务的特性脱颖而出!用户可以在自己的服务器上部署RustDesk服务端&…...
华为HarmonyOS 快速构建各种文本识别应用 -- 通用文字识别
适用场景 通用文字识别,是通过拍照、扫描等光学输入方式,将各种票据、卡证、表格、报刊、书籍等印刷品文字转化为图像信息,再利用文字识别技术将图像信息转化为计算机等设备可以使用的字符信息的技术。 可以对文档翻拍、街景翻拍等图片进行…...
shell作业
计算器 #!/bin/bash num1$1 num2$3 op$2 case $op in"")echo $((num1 num2));;"-")echo $((num1 - num2));;"*")echo $((num1 * num2));;"/")if [ $num2 -ne 0 ]; thenecho $((num1 / num2))elseecho "除数不能为0"fi;;*)…...
css部分
前面我们学习了HTML,但是HTML仅仅只是做数据的显示,页面的样式比较简陋,用户体验度不高,所以需要通过CSS来完成对页面的修饰,CSS就是页面的装饰者,给页面化妆,让它更好看。 1 层叠样式表&#…...
nginx 配置 跨域、压缩、文件下载 、静态文件、防盗链
1.跨域配置 在server模块下 访问如:http://127.0.0.1:8080/static/a.txt #跨域server {listen 8080;server_name localhost;#允许跨域请求的域, *代表所有add_header Access-Control-Allow-Origin *;#允许带上cookie请求add_header Access-Contro…...
SQL教程(1):什么是SQL?有什么用?
如果你是刚接触用户研究的新手,可能会听说过一个词叫做 SQL(Structured Query Language,结构化查询语言),但你可能还不太清楚它是什么,如何使用它,或者为什么它会对你的用户研究有帮助。别担心&…...
c++笔记2
14、c的对象 对象和结构的区别; 结构:包含各种类型的变量; 对象:包含各种函数、和变量; 设计对象的时候引用class关键字创建类,和结构形状差不多; 将变量称之为属性,函数称之为方…...
Xlsxwriter生成Excel文件时TypeError异常处理
在使用 XlsxWriter 生成 Excel 文件时,如果遇到 TypeError,通常是因为尝试写入的值或格式与 XlsxWriter 的限制或要求不兼容。 1、问题背景 在使用 Xlsxwriter 库生成 Excel 文件时,出现 TypeError: “expected string or buffer” 异常。此…...
应用层协议/传输层协议(UDP)
目录 应用层 如何自定义应用层协议? 序列化方式 1.基于行文本的方式来传输 2.基于xml的方式 3.基于json的方式 4.yml的形式 5.protobuffer(pb)形式 传输层 端口号 协议 UDP 校验和 CRC TCP TCP/IP五层协议 应用层 -- 传输层 -- 网络层 -- 数据链路层…...
【Linux】应用层协议—HTTP
一、HTTP协议介绍 请求-响应模型:HTTP (Hyper Text Transfer Protocol) 协议是基于请求和响应的。客户端(如Web浏览器)发送一个HTTP请求到服务器,服务器处理请求后返回一个HTTP响应。 无状态,无连接协议:H…...
使用Vue3+Echarts实现加载中国地图,点击省份地图下钻(完整教程)
一. 前言 在众多 ECharts 图表类型中,开发者始终绕不开的有各种各样的地图开发,关于地图开发,可能比其他图表相对繁琐一些,其实说简单也简单,说复杂也复杂,其中不乏有层级地图、3D 地图等,感觉…...
双目相机的标定,视差图,深度图,点云生成思路与实现。
该文档记录从双目相机标定到点云生成的所有过程,同时会附上代码。 代码直接能跑。https://github.com/stu-yzZ/stereoCamera 目录 大致思路如下: 一、相机标定 1、相机参数介绍 2、单目相机标定 3、双目相机标定 二、图片畸变矫正 三、极线矫正…...
解决 minio上传文件Service: S3, Status Code: 403
错误信息 [software.amazon.awssdk.services.s3.model.S3Exception: (Service: S3, Status Code: 403, Request ID: 180E9BC04F11312E, Extended Request ID: 81aefed089495c5faf6270c59bea93c9783926f74ef647fe6b17908f0976b557)]分析过程 4XX一般是客户端错误。403表示禁止…...
SpringBoot实战——个人博客项目
目录 一、项目简介 ?二、项目整体架构 数据库模块 后端模块 前端模块 ?三、项目具体展示 ?四、项目的具体实现 1、一些准备工作 ??数据库、数据表的创建 ??设置数据库和MyBatis的配置 ??将前端项目引入到当前项目中 2、登录注册模块 ??实体类的创建 ?…...
OpenCV-平滑图像
二维卷积(图像滤波) 与一维信号一样,图像也可以通过各种低通滤波器(LPF)、高通滤波器(HPF)等进行过滤。LPF 有助于消除噪音、模糊图像等。HPF 滤波器有助于在图像中找到边缘。 opencv 提供了函数 **cv.filter2D()**&…...
nodejs官方文档学习-笔记-1
一、异步工作 process.nextTick(): 回调会在当前操作完成后立即执行,但在事件循环进入下一个阶段之前。它是最先执行的。 Promise.then(): 回调会在 microtask 队列中执行,通常是在当前操作完成后,但在事件循环进入…...
YOLOv7
YOLOv7 是 YOLO 系列的一个重要版本,由 Chien-Yao Wang 和 Alexey Bochkovskiy 等研究人员和开发者提出,继续改进了 YOLOv4 和 YOLOv5 的设计,主要在模型性能、训练效率以及推理速度上进行优化。YOLOv7 进一步优化了 YOLO 系列在速度和精度之…...
汉语唤醒词的模糊判断(Python)
汉语唤醒词的模糊判断【Python】 说明安装库代码Demo其他 说明 这是一个简单的汉语模糊唤醒词的判断器,汉语发音中前后舌以及声母韵母的区别,如果进行精准判断,很容易误判。需要一个模糊判断的逻辑! 安装库 pip install pypinyin代码Demo …...
C语言实例_26之根据输入计算星期几
1. 问题 请输入星期几的第一个字母来判断一下是星期几,如果第一个字母一样,则继续判断第二个字母。 2. 实现思路 思路一:嵌套if-else语句实现 首先接收用户输入的第一个字母,使用if-else语句进行判断。如果输入的是M࿰…...
包管理器npm,cnpm,yarn和pnpm
npm (Node Package Manager) 核心技术与工作原理 依赖解析: 广度优先搜索(BFS):npm 使用 BFS 算法来解析依赖树,尽量扁平化 node_modules 目录以减少重复的依赖项。冲突处理:如果两个包需要同一个依赖的不…...
docker-compose部署skywalking 8.1.0
一、下载镜像 #注意 skywalking-oap-server和skywalking java agent版本强关联,版本需要保持一致性 docker pull elasticsearch:7.9.0 docker pull apache/skywalking-oap-server:8.1.0-es7 docker pull apache/skywalking-ui:8.1.0二、部署文件docker-compose.yam…...
Android -- [SelfView] 自定义多行歌词滚动显示器
Android – [SelfView] 自定义多行歌词滚动显示器 流畅、丝滑的滚动歌词控件* 1. 背景透明;* 2. 外部可控制进度变化;* 3. 支持屏幕拖动调节进度(回调给外部);效果 歌词文件(.lrc) 一. 使用…...
记事本建java及java命名规范
1.桌面开发:c# 2. 记事本建java: 以class的名称(类名)为名,名称.java 编译jdk:javac 名称.java 调动运行jre : java 名称 查看名称.java里面的内容:cat 名称.java java 的命名规范 大驼峰(每个单词首…...
Elasticsearch scroll 之滚动查询
Elasticsearch scroll 之滚动查询 Elasticsearch 的 Scroll API 是一种用于处理大规模数据集的机制,特别是在需要从索引中检索大量数据时。通常情况下,Elasticsearch 的搜索请求会有一个结果集大小的限制 (fromsize 的检索数量默认是 10,000 条记录)&am…...
cartographer建图与定位应用
文章目录 前言一、安装cartographer1.安装环境2.源码编译2.1 下载2.2 编译 二、gazebo仿真2d建图0.准备仿真环境1.编写lua文件2.编写启动文件3.建图保存 三、cartographer定位 move_base导航3.1 编写启动文件3.2 启动launch 总结 前言 本文介绍cartographer在ubuntu18.04下的…...
CEEMDAN-CPO-VMD二次分解(CEEMDAN+冠豪猪优化算法CPO优化VMD)
CEEMDAN-CPO-VMD二次分解(CEEMDAN冠豪猪优化算法CPO优化VMD) 目录 CEEMDAN-CPO-VMD二次分解(CEEMDAN冠豪猪优化算法CPO优化VMD)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 首先运用CEEMDAN对数据进行一次分解ÿ…...
【Golang】Go语言编程思想(二):函数式编程
函数式编程 函数与闭包 支持函数式编程的语言当中,函数是一等公民,参数、变量、返回值都可以是函数。 以 adder 为例,下例实现了一个函数式编程: package mainimport "fmt"func adder() func(int) int {sum : 0retu…...
ros项目dual_arm_pick-place(moveit和gazebo联合仿真)(一)
目录 前言正文创建功能包具体代码运行 总结 前言 dual_arm_pick-place项目中,实现了两套的moveit和gazebo联合仿真。 启动文件分别是bringup_moveit.launch和arm_bringup_moveit.launch。 在这个项目中,我将代码重新创建了一个包,co_simula…...
点线面|点到两点直线距离求解
在点云库(PCL)中,计算点到直线的距离可以借助于向量运算。以下是这种计算方法的步骤以及相应的实现代码。 概念 要计算点 AA 到通过点 BB 和 CC 定义的直线的距离,可以使用以下步骤: 定义两个向量: 向量 …...
等保2.0三级测评华为华三交换机路由器
在使用本博客提供的学习笔记及相关内容时,请注意以下免责声明: 信息准确性:本博客的内容是基于作者的个人理解和经验,尽力确保信息的准确性和时效性,但不保证所有信息都完全正确或最新。 非专业建议:博客中的内容仅供参考,不能替代专业人士的意见和建议。在做出任何重要…...
鸿蒙分享(二):引入zrouter路由跳转+封装
码仓库:https://gitee.com/linguanzhong/share_harmonyos 鸿蒙api:12 鸿蒙第三方库地址:OpenHarmony三方库中心仓 zrouter地址:OpenHarmony三方库中心仓 1.引入zrouter 1.打开终端界面:输入 ohpm install hzw/zrouter 2.在项目…...
欧拉角和旋转矩阵之间的转换
在实际编程实践中,特别是使用C的Eigen库时,需要掌握多种空间旋转表示之间的转换。本文将深入探讨欧拉角与旋转矩阵之间的相互转换,这是使用库时常见的操作。 首先,让我们了解欧拉角。它是指通过围绕三个基本坐标轴旋转来表示三维对…...
Typora的激活
Typora的安装、激活 图文教程 | 2024年最新Typora激活使用教程合集 Typora 激活指南(2024年最新版) 图文教程 | 2024Typora最新版免费激活使用教程(新旧版可用)...
排查bug的通用思路
⭐️前言⭐️ APP点击某个按钮没有反应/PC端执行某个操作后,响应较慢,通用的问题排查方法: 从多个角度来排查问题 🍉欢迎点赞 👍 收藏 ⭐留言评论 🍉博主将持续更新学习记录收获,友友们有任何问题可以在评…...
探秘CSS3 3D转换:打造立体网页动画与视觉效果的魔法
在现代网页设计中,CSS3 3D转换(3D Transforms)已经成为一种强大的工具,用于创建各种动态效果和视觉吸引力。本文将详细介绍CSS3 3D转换的基本概念、常见属性及其应用示例,帮助您掌握这一技术,提升网页设计的…...
Pycharm 安装两种不同版本的解释器
背景 Pycharm使用的解释器是python3.11,python3.9.x之后不支持win7;有同事需要在win7上使用python制作的exe,而了“win7环境下运行python打包exe提示找不到api-ms-win-core-path-l1-1-0.dll”。 过程 查找了一些资料,发现直接将…...
小红书登顶87国App Store 实时翻译功能在开发了
1月15日,第三方数据平台七麦数据显示,截至发稿,小红书在87个国家登上App Store的免费排行榜第一,包括美国、加拿大、澳大利亚、英国、意大利等国家。此外,它还在39个国家的App Store免费排行榜上位列第2至第10位。一位小红书员工告诉经济观察网,社区技术部门的其中一个组…...
尹锡悦被捕后,哀嚎与欢呼在总统官邸前同时上演
《朝鲜日报》1月15日报道,韩国总统尹锡悦于15日上午10点33分因涉嫌内乱罪被逮捕。尹锡悦乘坐警卫车前往检察厅所在地京畿道果川政府办公大楼。随着尹锡悦被捕的消息传开,尹锡悦支持者们聚集在首尔龙山区汉南洞总统官邸前,嚎啕大哭、怒不可遏。反尹抗议者们则陷入了狂欢的气氛…...
肖志夫:白宫称美印加强国防战略尖端技术合作不针对中国
美东时间2025年1月6日,美国白宫网站发布声明:今天,美国国家安全顾问杰克·沙利文(APNSA Sullivan)在新德里会见了印度国家安全顾问阿吉特·多瓦尔(NSA Doval)、印度外交部长s·贾伊尚卡尔和总理莫迪,美国和印度继续加强国防战略尖端技术…...
胡伟武:跟在美国后面爬行,解决不了卡脖子的问题
1. 2024年,是中国科技成果井喷的一年,无人机、机器狗、六代机……看得人目不暇接,根本停不下来。其中,中国学者、学术界的科研人员做了多大的贡献呢?龙芯的创始人胡伟武关于中国学者,他是这么说的:中国学者喜欢跟随美国做研究,中国学者脱离本国产业…...
后沙:1962中印边境战争,打断了印度工业化道路?
2025新年第一天,英国《金融时报》就给印度经济泼了一大盆冷水。文章很长,说“印度经济已经失去动力”,论据是外资正在纷纷逃离印度……股市上的“印度热”正在迅速降温,印度面临着一大堆问题,如经济活动混乱、高…...
泽连斯基:支持在乌克兰部署欧洲部队,乌不放弃加入北约
当地时间1月2日晚间,乌克兰总统泽连斯基在接受当地媒体采访时表示,乌克兰支持法国提出的在乌克兰部署欧洲部队的倡议,但仅有法国是不够的,乌克兰不希望这一倡议仅由一到两个国家实施。同时,乌克兰认为这一倡议必须是迈向北约的一步,部署欧洲部队不能意味着乌克兰未来无法…...