Qt音樂播放器項目實踐:本地持久化與邊角問題處理

本音樂播放器完整項目源碼(包含各個按鈕的圖片文件):

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.…