本音乐播放器完整项目源码(包含各个按钮的图片文件):

ly/Project-Code - Gitee.com

一.本地持久化

请注意,学习此部分之前需要读者具有一定的Mysql基础。如果读者能够接受无法本地持久化,那么可以跳过这部分内容,直接去看边角问题处理。我们这里使用SQLite数据库进行本地持久化保存,因为它在使用时不需要配置任何环境。而且我们也只会用一些简单的增删改查,下面是SQLite的教程:

SQLite 教程 | 菜鸟教程

Qt中已经内置了SQLite,在安装qt开发环境时,SQLite环境已经配置好了,⽤⼾在.pro⽂件中导⼊数据库模块就可以使⽤。

// *.pro文件中添加模块
QT += sql

1.1QSqlDatabases类的介绍

QSqlDatabase类主要处理与数据库的连接,它提供了创建、配置、打开和关闭数据库连接的⽅法。
数据库的连接和关闭:

// 功能:根据type来添加数据库驱动
// type:数据库类型 [QDB2:IBM DB2, QMYSQL:MySQL, QOCI:Oracle, QODBC:ODBC,
QSQLITE:SQLite...]
// connectionName: 数据库连接的名称[可选]。如果提供,可以为数据库连接指定⼀个唯⼀的名称
// 返回值:表⽰新创建的数据库连接
static QSqlDatabase addDatabase(const QString &type,const QString &connectionName =QLatin1String(defaultConnection))// 添加SQLite数据库驱动,返回⼀个连接
QSqlDatabase QQMusicDB = QSqlDatabase::addDatabase("QSQLITE");// 功能:设置数据库⽂件的名称
// name: 要连接的数据库的名称。对于SQLite,通常是数据库⽂件的路径;对于其他数据库系统,⽐
如MySQL通常是数据库管理系统中数据库的名称
void setDatabaseName(const QString &name);// 功能:打开数据库连接,即和数据库真正建⽴连接
// 返回值:连接成功连接返回true,否则返回false,注意:可以使⽤isopen()⽅法检测是否打开
// user:数据库⽤⼾名
// password: 数据库密码
bool open();
bool open(const QString &user, const QString &password);// 功能:关闭数据库连接,释放所有资源,并使与数据库⼀起使⽤的所有QSqlQuery对象⽆效
void close();

1.2本文会用到的SqLite的数据类型

数据类型描述
NULL值是⼀个NULL值
INTEGER值是⼀个带符号的整数,根据值的⼤⼩存储在1 2 3 4 6 或 8 字节中
REAL值是⼀个浮点数,存储为8字节的IEEE浮点数
TEXT值是⼀个⽂本字符串,使⽤数据库编码(UTF-8、UTF-16BE 或 UTF-6LE)存储

1.3QSqlQuery类的介绍

// 功能:准备SQL语句,该语句中包含⼀个或者多个参数占位符。这些参数占位符在SQL中默认为?表
⽰
// 也可以⾃定义占位符。其允许提前先设置好SQL语句结构,但是不执⾏
bool prepare(const QString &query);// 功能:使⽤参数的名称(即通过prepare构造SQL语句时设置的占位符)来绑定值
// placeholder: 参数占位符的名称
// val:要绑定的值
void bindValue(const QString &placeholder,const QVariant &val,QSql::ParamType paramType = QSql::In);// 功能:通过位置来帮实际值
// pos: 是参数的位置,从0开始计数
// val: 要绑定的值
void QSqlQuery::bindValue(int pos,const QVariant &val,QSql::ParamType paramType = QSql::In);// 功能:按照建表时成员的顺序绑定
void addBindValue(const QVariant &val,
QSql::ParamType paramType = QSql::In);

基本的用法是:通过prepare先将SQL语句准备好,在准备时实际值可以先⽤其他符号占⽤,然后通过bindValue来绑定实际值,通过命名绑定和位置绑定都可以,绑定好之后,调⽤exec执⾏。

而构造好SQL语句,使⽤QSQLQuery的对象query执⾏SQL语句,查询结果可以通过query获取:

// 功能:将查询结果的当前⾏指针向后移动⼀⾏,如果移动后有记录则返回true,否则返回false
// 利⽤该⽅法,搭配while循环,可获取到所有查询记录
bool next();// 功能:获取查询记录中索引为index的域的值
// 查询结果按照select语句后所查询字段顺序,从左往右基于0开始编号,依次递增
// select name, age, gpa from student;
// 每条查询结果中,name的索引为0 age的索引为1, gpa的索引为2
QVariant value(int index) const;// 功能:根据查询记录中,name字段对应的值,如果名字不匹配,将返回⼀个⾮法的QVariant
QVariant value(const QString &name) const;

1.4数据库创建思路概述

        我们这里是对导入的音乐以及是否喜欢和最近播放进行本地持久化。那么还记得我们之前有一张musicList表存储在主界面类中吗?无论歌曲的我喜欢状态还是最近播放状态被改变,都会反映到这张表中。所以我们只需要在程序结束时,将该musicList中的所有歌曲的各项属性加载到数据库中。然后程序再次启动时读取数据库,填充musicList列表然后刷新三张CommonPage页面即可。

为了避免歌曲重复加载,我们这里给MusicList加一个set表维护歌曲的所有路径:

//MusicList::addMusicsByUrls新增
if(fileType == "audio/mpeg" || fileType == "audio/flac" || fileType == "audio/wav")
{if(!filePaths.contains(url.toLocalFile()))//新增部分{musicList.push_back(Music(url));//添加到哈希集合中filePaths.insert(url.toLocalFile());}
}//添加成员变量
QSet<QString> filePaths;

1.5本地持久化实现

首先我们给主界面函数新增一个initDb的方法用来初始化数据库以及程序与数据库的连接:

void SekaiMusic::initDb()
{//设置我喜欢,本地下载,最近播放的文本和图片ui->likePage->setCommonPageImage(":/images/ilikebg.png","我喜欢");ui->localPage->setCommonPageImage(":/images/localbg.png","本地音乐");ui->recentPage->setCommonPageImage(":/images/recentbg.png","最近播放");//设置页面类型ui->likePage->setPageType(PageType::LIKE_PAGE);ui->localPage->setPageType(PageType::LOCAL_PAGE);ui->recentPage->setPageType(PageType::RECENT_PAGE);//连接数据库sekaiMusicDb = QSqlDatabase::addDatabase("QSQLITE");//添加数据库驱动sekaiMusicDb.setDatabaseName("SekaiMusic.db");if(!sekaiMusicDb.open()){qDebug() << "数据库打开出错:" << sekaiMusicDb.lastError().text();return;}QSqlQuery query;query.prepare("create table if not exists MusicInfo( \id integer primary key autoincrement,\musicId varchar(50) unique,\musicName varchar(50),\singerName varchar(50),\albumName varchar(50),\duration bigint,\isLike integer,\isHistory integer,\musicPath varchar(256));");if(!query.exec()){qDebug() << "数据库初始化错误" << query.lastError().text();return;}qDebug() << "数据库表创建/连接成功!!";
}

顺带把三个CommonPage的初始化工作也放到这个函数中。接下来我们添加initMusicList函数,让musicList通过读取数据库来初始化播放列表:

void SekaiMusic::initMusicList()
{musicList.loadMusicOfDb();ui->likePage->reFresh(musicList);ui->localPage->reFresh(musicList);ui->recentPage->reFresh(musicList);
}void MusicList::loadMusicOfDb()
{QSqlQuery query;query.prepare("select musicId,musicName,singerName,albumName,duration,isLike,isHistory,musicPath from MusicInfo");if(!query.exec()){qDebug() << "数据库表查询失败" << query.lastError().text();return;}qDebug() << "表查询成功";while(query.next()){//删除失效数据if(!QFileInfo::exists(query.value("musicPath").toString())){QSqlQuery query_delete;query_delete.prepare("delete from MusicInfo where musicId = ?");query_delete.addBindValue(query.value("musicId").toString());if(!query_delete.exec()){qDebug() << "失效数据删除失败" << query_delete.lastError().text();}elseqDebug() << "失效数据删除成功";continue;}//说明数据存在Music music;music.setMusicId(query.value(0).toString());music.setMusicName(query.value(1).toString());music.setSingerName(query.value(2).toString());music.setAlbumName(query.value(3).toString());music.setDuration(query.value(4).toLongLong());music.setIsLike(query.value(5).toInt() == 1);music.setIsHistory(query.value(6).toInt() == 1);music.setMusicUrl(QUrl::fromLocalFile(query.value(7).toString()));musicList.push_back(music);filePaths.insert(music.getMusicUrl().toLocalFile());//插入到哈希集合中}
}

接下来当程序关闭时我们让musicList自己把所有的music数据写入/更新到数据库中,当然这个函数放到关闭窗口按钮的槽函数中执行:

//关闭按钮的槽函数中//更新数据库musicList.updateMusicOfDb();//关闭数据库sekaiMusicDb.close();this->close();void MusicList::updateMusicOfDb()
{for(auto& music : musicList){music.insertSelfOfDb();}
}void Music::insertSelfOfDb()
{QSqlQuery query;query.prepare("SELECT EXISTS (SELECT 1 FROM MusicInfo WHERE musicId = ?)");query.addBindValue(musicId);if(!query.exec()){qDebug()<<"查询失败: "<<query.lastError().text();return;}if(query.next()){bool isExists = query.value(0).toBool();if(isExists){//说明数据之前已经插入到数据库中了//不需要再插入music对象,此时只需要将isLike和isHistory属性进行更新query.prepare("UPDATE MusicInfo SET isLike = ?, isHistory = ? WHERE musicId = ?");query.addBindValue(isLike? 1 : 0);query.addBindValue(isHistory? 1 : 0);query.addBindValue(musicId);if(!query.exec()){qDebug()<<"更新失败: "<<query.lastError().text();}qDebug()<<"更新music信息: "<<musicName<<" "<<musicId;}else{//说明该歌曲之前没有被插入到数据库中query.prepare("insert into MusicInfo (musicId,musicName,singerName,albumName,duration,isLike,isHistory,musicPath) values(?,?,?,?,?,?,?,?);");query.addBindValue(musicId);query.addBindValue(musicName);query.addBindValue(singerName);query.addBindValue(albumName);query.addBindValue(duration);query.addBindValue(isLike ? 1 : 0);query.addBindValue(isHistory? 1 : 0);query.addBindValue(musicUrl.toLocalFile());if(!query.exec()){qDebug()<<"插入失败: "<<query.lastError().text();return;}qDebug()<<"插入music信息: "<<musicName<<" "<<musicId;}}
}

这样当我们第一次把歌曲信息加载到程序中,第二次再打开程序时就不需要再去重复导入了,同时如果第二次打开程序时,本地文件被删除了,那么数据库会自动把失效数据删除,不再让其导入到播放列表中。

二.边角问题处理

2.1最大化,最小化和换肤问题处理

最大化因为我们之前再设计Ui界面时有些空间的尺寸是写死的,比如按钮图标30*30,所以如果要最大化,需要我们自己去做适配。这里不再介绍。当然换肤问题也需要自己再去做适配。所以我们只处理最小化的情况,只需要调用一个函数即可:

//边角问题处理
void SekaiMusic::on_min_clicked()
{showMinimized();
}void SekaiMusic::on_max_clicked()
{QMessageBox::information(this,"温馨提示","「最大化」功能加载中... ███████░ 90%,\n抱歉,不是卡了,是我们的CPU正在为您的体验全力燃烧。");
}void SekaiMusic::on_skin_clicked()
{QMessageBox::information(this,"温馨提示","皮肤功能正在骑马赶来的路上~");
}

2.2添加系统托盘

我们一般见到的音乐软件,都是点击关闭按钮后不会立即关闭窗口而是缩小到系统托盘中,所以我们这里也为我们的程序添加一个这样的效果:

//SekaiMusic中新增成员变量:QSystemTrayIcon* trayIcon;//系统托盘//initUi中新增//初始化系统托盘trayIcon = new QSystemTrayIcon(this);trayIcon->setIcon(QIcon(":/images/tubiao.png"));trayIcon->setToolTip("SekaiMusic");//创建托盘菜单QMenu* trayMenu = new QMenu(this);trayMenu->addAction("还原窗口",this,&SekaiMusic::showWindows);trayMenu->addSeparator();trayMenu->addAction("关闭窗口",this,&SekaiMusic::closeWindows);trayIcon->setContextMenu(trayMenu);//在关闭窗口时显示系统托盘,点击还原窗口时隐藏
void SekaiMusic::on_quit_clicked()
{hide();//隐藏主窗口trayIcon->show();//最小化到系统托盘
}void SekaiMusic::showWindows()
{show();//同时隐藏系统托盘trayIcon->hide();
}void SekaiMusic::closeWindows()
{//更新数据库musicList.updateMusicOfDb();//关闭数据库sekaiMusicDb.close();this->close();
}

2.3保证程序运行时只有一个实例

我们这里禁止程序启动多次,一般也不需要,多个实例同时运⾏有以下缺陷:

◦ 多个实例同时运⾏可能会导致资源浪费,如内存、CPU效率等

◦ 如果应⽤程序涉及对共享数据的修改,多个程序同时运⾏可能会导致数据不⼀致问题

◦ 若多个实例尝试访问同⼀资源时,如⽂件、数据库等,可能会导致冲突或错误

◦ 另外,⽤⼾体验不是很好,多个实例操作时容易混淆

因此有时会禁⽌程序多开,即⼀个应⽤程序只能运⾏⼀个实例,也称为单实例应⽤程序或单例应⽤程序。在Qt中,禁⽌程序多开的⽅式有好⼏种,此处采⽤共享内存实现。

共享内存是操作系统中的概念,是进程间通信的⼀种机制。由于相同key值的共享内存只能存在⼀份,因此在程序启动时可以检测共享内存是否已经被创建,如果已经创建则说明程序已经在运⾏,否则程序还没有运⾏。

//修改main.cpp为如下内容
#include "sekaimusic.h"#include <QApplication>
#include <QSharedMemory>int main(int argc, char *argv[])
{QApplication a(argc, argv);// 创建共享内存-确保程序只有一个实例运行QSharedMemory sharedMem("SekaiMusic");// 如果共享内存已经被占⽤,说明已经有实例在运⾏if (sharedMem.attach()) {QMessageBox::information(nullptr, "SekaiMusic", "SekaiMusic已经在运⾏...");sharedMem.detach();return 0;}sharedMem.create(1);//当然这1字节的内存空间也需要我们手动去释放,否则除非电脑重启,这个共享内存会一直存在//连接 aboutToQuit 信号确保资源释放QObject::connect(qApp, &QCoreApplication::aboutToQuit, [&sharedMem]() {if (sharedMem.isAttached()) {sharedMem.detach();qDebug() << "共享内存已正确释放";}});SekaiMusic w;w.show();return a.exec();
}

2.4解决界面偶尔乱移动的问题

我们之前解决窗口无法拖动的问题是这样子去解决的:

void SekaiMusic::mouseMoveEvent(QMouseEvent *event)
{if(event->buttons() == Qt::LeftButton){//注button无法处理移动事件,buttons更为合适,可以参考官方文档this->move(event->globalPos() - dragPosition);return;}//其他事件默认处理QWidget::mouseMoveEvent(event);
}void SekaiMusic::mousePressEvent(QMouseEvent *event)
{//判断左键同时判断鼠标是否在窗口内if(event->button() == Qt::LeftButton){//记录相对位置dragPosition = event->globalPos() - frameGeometry().topLeft();return;}//其他事件默认处理QWidget::mousePressEvent(event);
}

这样会有一个问题,如果我们鼠标按下却没有拖动怎么办,那么下一次我们不小心拖一下他就会乱移动。因为我们拖动时会有三个动作:按下-拖动-释放,所以我们可以添加一个标记isDragging,然后将原来的代码改为如下代码即可解决问题:

void SekaiMusic::mouseMoveEvent(QMouseEvent *event)
{if(event->buttons() == Qt::LeftButton){// 如果是第一次移动(还未记录初始相对位置),则记录初始相对位置if (!isDragging) {dragPosition = event->globalPos() - frameGeometry().topLeft();isDragging = true;}// 移动窗口this->move(event->globalPos() - dragPosition);return;}// 其他事件默认处理QWidget::mouseMoveEvent(event);
}void SekaiMusic::mousePressEvent(QMouseEvent *event)
{// 判断左键if(event->button() == Qt::LeftButton){// 仅标记左键按下,不记录位置isDragging = false;return;}// 其他事件默认处理QWidget::mousePressEvent(event);
}void SekaiMusic::mouseReleaseEvent(QMouseEvent *event)
{// 鼠标释放时重置标志位if (event->button() == Qt::LeftButton) {isDragging = false;}QWidget::mouseReleaseEvent(event);
}

2.5禁止qDebug()输出

要逐个删除程序中qDebug的打印太⿇烦,可以在配置⽂件中通过添加以下语句,禁⽌qDebug输出:

# ban qDebug output
DEFINES += QT_NO_DEBUG_OUTPUT

2.6对程序进行打包

Qt可执⾏程序在运⾏的时候,需要依赖Qt框架中的⼀些库⽂件,如果对⽅及其上之前未安装Qt环境,点击可执⾏程序运⾏时,会提⽰缺少xxx.dll动态库信息等。为了让开发好的Qt可执⾏程序在未安装Qt环境的机器上也可以运⾏,就需要对项⽬进⾏打包,打包的过程会将exe可执⾏程序运⾏时所需的依赖⽂件全部整合到⼀起,将打包好的包⼀起发给对端,双击exe可执⾏程序时就可以执⾏。

注意:打包时exe需要⽤release版本,debug是调试版本,release版本编译器会去除调试信息,并会对⼯程进⾏优化等操作,使程序体积更⼩,运⾏效率更⾼。

2.6.1windeployqt打包⼯具

windeployqt 是 Qt 提供的⼀个⼯具,⽤于⾃动收集并复制运⾏ Qt 应⽤程序所需的动态链接库(.dll ⽂件)及其他资源(如插件、QML 模块等)到可执⾏⽂件所在的⽬录。这样你就可以将应⽤程序和这些依赖项⼀起打包,确保在没有 Qt 环境的其他机器上也能运⾏。

【主要功能】

  • ⾃动收集依赖项: windeployqt 会分析你的 Qt 应⽤程序,确定它所依赖的 Qt 库⽂件(如Qt6Core.dll, Qt6Widgets.dll),并将这些⽂件复制到应⽤程序的⽬录。
  • 处理插件和QML模块: 如果你的应⽤程序使⽤了 Qt 的插件(如平台插件 qwindows.dll 或图形驱动插件等),windeployqt 也会将这些插件⼀并打包。对于使⽤ QML 的应⽤程序,它也会⾃动收集必要的 QML 模块。
  • 处理资源⽂件: 如果你的应⽤程序包含了 Qt 的资源⽂件(如图标、翻译⽂件等),它也会确保这些资源正确包含在最终的应⽤程序中。

2.6.2打包流程

  1. 配置好Qt环境变量
  2. 选择以release⽅式编译程序。编译好之后,在⼯程⽬录上⼀层会⽣成包含release字段的⽂件夹,⽂件夹内部就有release模式的可执⾏程序。
  3. 将新建⼀个⽂件夹,命名为SekaiMusic,将release模式可执⾏程序拷⻉到SekaiMusic。
  4. 进⼊SekaiMusic,在该⽂件夹内部,按shift,然后⿏标右键单击,弹出菜单中选择"在此处打开Powershell 窗⼝(S)",在弹出窗⼝中输⼊ windeployqt .\SekaiMusic.exe,windeployqt⼯具就会⾃动完成打包。

如果不想要仅仅只是压缩包的形式,可以参考这位博主的文章将我们自己写的程序打包为安装包:

Qt入门(三):项目打包_qt打包-CSDN博客

这里我们不再介绍,上面的四个步骤结束时将该⽬录压缩之后,发给对⽅,对⽅收到之后直接解压,点击exe之后就可以运⾏。

其他的边角问题,读者可以自行进行解决,上面边角问题解决之后基本上已经没有大问题了(当然博主感觉应该是没有什么大问题了,你要说程序运行过程中你把歌曲文件删了碰到的问题,也是个问题,但是博主这里便不再介绍如何解决了,毕竟我们这是个练手项目,不是长时间运营的项目)

到此,我们的音乐播放器项目已经完成。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于NB-IoT技术的宠物定位跟踪系统设计#基于STM32\物联网\单片机技术的宠物定位跟踪系统

基于NB-IoT技术的宠物定位跟踪系统设计#基于STM32\物联网\单片机技术的宠物定位跟踪系统在设计基于NB-IoT技术的宠物定位跟踪系统时&#xff0c;首先明确了系统分为感知层、网络层和应用层三个部分。在感知层&#xff0c;考虑到需要获取宠物位置和运动状态&#xff0c;选用GPS定…

【入门级-算法-3、基础算法:递归法】

递归是一种非常重要的算法思想&#xff0c;它指的是函数调用自身的过程。递归通常包含两个主要部分&#xff1a;基线条件&#xff08;终止条件&#xff09;和递归条件&#xff08;调用自身的条件&#xff09;。 下面通过例子来理解递归算法&#xff1a; 计算阶乘 阶乘的递归定义…

【CS创世SD NAND征文】存储芯片在工业电表中的应用与技术演进

【CS创世SD NAND征文】存储芯片在工业电表中的应用与技术演进1.工业电表的市场背景2.技术方案分析3.核心技术特性3.1.主控芯片&#xff1a;APM32F465VET63.3.存储芯片&#xff1a;CSNP4GCR01-DPW3.3.1. 基本概述3.3.2. 核心特性3.3.3. 优势特点3.3.4 四大管理算法4.存储芯片性能…

建筑施工遮挡场景漏检率↓76%:陌讯动态融合算法实战解析

原创声明 本文为原创内容&#xff0c;技术参数及架构解析引用自《陌讯技术白皮书》&#xff0c;未经授权禁止转载。 一、行业痛点&#xff1a;建筑施工安全监控的 "看得见" 与 "看不准" 建筑施工场景的安全监控长期面临双重挑战&#xff1a;一方面&…

【LeetCode题解】LeetCode 209. 长度最小的子数组

【题目链接】 209. 长度最小的子数组 【题目描述】 【题解】 方法一&#xff1a;滑动窗口 本题可以使用双指针算法&#xff0c;定义两个指针l和r分别表示子数组的开始位置和起始位置&#xff0c;sum数组存储的从l到r区间内所有元素的和。初始状态下&#xff0c;l和r都指向下…

2025-08-21 Python进阶6——迭代器生成器与with

文章目录1 迭代器与生成器1.1 迭代器1.1.1 基本使用1.1.2 手动迭代&#xff08;带异常处理&#xff09;1.1.3 自定义迭代器1.2 生成器1.2.1 工作原理1.2.2 斐波那契数列示例1.3 推导式1.3.1 列表推导式1.3.2 字典推导式1.3.3 集合推导式1.4.4 元组推导式&#xff08;生成器表达…

C++——C++重点知识点复习2(详细复习模板,继承)

目录 模板 函数模板 类模板 非类型模板参数 模板的特化 函数模板特化 类模板的特化 为什么普通函数可以分离&#xff1f; 继承 继承概念 基类和派生类对象赋值转换&#xff08;切割&#xff0c;切片&#xff09; 隐藏 派生类的默认成员函数 .复杂的菱形继承及菱形…

python 项目编号 2025821 有关于中英文数据的收集、处理

python专栏记录&#xff1a;前言 批量读取单词 JSON 文件 → 解析出单词、释义、例句、短语 → 数据清洗&#xff08;去掉特殊符号&#xff09; → 同步更新到 MySQL 数据库。 内容 import json import pymysql import re import time from pymysql.converters import escape_s…

Document Solutions .NET Bundle 8.2.0

Document Solutions .NET Bundle 8.2.0MESCIUS 的 Document Solutions .NET Bundle 是一套完整的 API 和查看工具&#xff0c;可增强文档处理并提高效率。它包含 Excel、Word、PDF 和图像文档&#xff0c;以及 PDF 查看器、数据查看器和图像查看器的标准许可证。它将强大的 .NE…

在职老D渗透日记day20:sqli-labs靶场通关(第27关)get报错注入 过滤select和union ‘闭合

5.27.第27关 get报错注入 过滤select和union 闭合function blacklist($id) { $id preg_replace(/[\/\*]/,"", $id); //strip out /* $id preg_replace(/[--]/,"", $id); //Strip out --. $id preg_replace(/[#]/,"", $id); //Strip out #. $…

Go 并发编程-channel

channel 文章目录channel简介基本概念类型表示法值表示法操作的特性初始化通道接收元素值Happens before发送值例1核心组件关键执行顺序输出示例&#xff08;可能顺序&#xff09;设计要点例2例3关闭通道长度与容量单向通道主要用途增强代码表达性和安全性&#xff08;最重要的…

开源和免费一样吗?以商城系统为例为您分析~

开源和免费并不完全一样&#xff0c;二者在核心定义、权利范围和实际应用中存在显著区别&#xff0c;具体可以从以下几个方面理解&#xff1a; 1. 核心定义不同开源&#xff08;Open Source&#xff09;&#xff1a; 指软件的源代码是公开可获取的&#xff0c;任何人都可以查看…

CMOS知识点 MOS管饱和区电流公式

知识点16&#xff1a;同上篇一样&#xff0c;MOS管主要有3个工作区域&#xff1a;截止区&#xff08;Cut-off Region&#xff09;&#xff1a; < &#xff0c;没有沟道形成&#xff0c;几乎没有电流。线性区/三极管区&#xff08;Triode Region&#xff09;&#xff1a; &g…

【集合框架LinkedList底层添加元素机制】

在 Java 集合框架中&#xff0c;LinkedList 与 ArrayList 是两种截然不同的线性表实现。如果说 ArrayList 像一个可以伸缩的“盒子阵列”&#xff0c;那么 LinkedList 就像一条由“节点”串联而成的“双向链条”。今天&#xff0c;我们将深入 LinkedList 的源码&#xff0c;一步…

《P2700 逐个击破》

题目背景三大战役的平津战场上&#xff0c;傅作义集团在以北平、天津为中心&#xff0c;东起唐山西至张家口的铁路线上摆起了一字长蛇阵&#xff0c;并企图在溃败时从海上南逃或向西逃窜。为了就地歼敌不让其逃走&#xff0c;指挥官制定了先切断敌人东西两头退路然后再逐个歼灭…

C6.0:晶体管放大器的原理与应用(基极偏置篇)

将晶体管Q点偏置在负载线中点附近后&#xff0c;如果将一个小的交流信号耦合到基极上&#xff0c;便会产生一个交流的集电极电压&#xff0c;交流集电极电压与交流基极电压波形相似&#xff0c;但是幅度要大了很多&#xff0c;即交流集电极电压是对交流基极电压的放大。本篇学习…

Oracle: cannot decrease column length because some value is too big

1.背景今天项目上查不到数据,查库发现默认20位的字段被改为了200,用的还是char类型&#xff0c;填充了一堆空格 2.知识LENGTH() 函数用于计算字符串字段 长度TRIM() 函数用于去除字符串字段 column 前后的空格&#xff08;默认&#xff09;或指定字符&#xff1a;SUBSTR() 用于…

Elasticsearch 写入全链路:从单机到集群

0. 先把术语摆正 Index&#xff08;索引&#xff09;&#xff1a;逻辑数据集合&#xff0c;≈ MySQL 的库。Document&#xff08;文档&#xff09;&#xff1a;一条 JSON 数据&#xff0c;≈ MySQL 的行。Field&#xff08;字段&#xff09;&#xff1a;文档里的键值&#xff0…

Java多线程编程——基础篇

目录 前言 一、进程与线程 1、进程 2、线程 二、并发与并行 1、并发 2、并行 三、线程调度 1、CPU时间片 2、调度方式 ①时间片轮转 ②抢占式调度 四、线程实现方式 1、继承 Thread 类 Thread的多种构造函数&#xff1a; 2、实现 Runnable 接口 五、线程的核心方法 1、start() …

阿里云的centos8 服务器安装MySQL 8.0

在 CentOS 8 上安装 MySQL 8.0 可以通过添加 MySQL 官方 YUM 仓库并使用 dnf 命令安装。以下是具体步骤&#xff1a; 步骤如下&#xff1a; 下载并添加 MySQL 官方 YUM 仓库 运行以下命令下载 MySQL 8.0 的 YUM 仓库配置文件&#xff1a; sudo dnf install https://dev.mysql.…