QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2
在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音视频以其直观、生动的特性,成为信息传播与娱乐休闲的重要媒介。而在这繁华音视频世界的幕后,有一位低调而强大的“魔法工匠”——FFmpeg。
FFmpeg 是一款声名远扬的开源音视频处理工具集,凭借其跨平台、功能强大等显著优势,在音视频领域占据着不可撼动的地位。它就像一个拥有无数神奇工具的百宝箱,集音视频的录制、编解码、转换、流媒体传输等众多功能于一身,为音视频内容的创作、传播与消费提供了坚实的技术支撑。
对于开发者来说,FFmpeg 宛如一座取之不尽的宝藏。它提供了丰富且易用的 API 接口,能够无缝集成到各类应用程序中。无论是开发一个简洁实用的音视频播放器,还是打造一个功能全面的专业视频编辑软件,FFmpeg 都能凭借其强大的功能和高效的性能,助力开发者快速实现目标,大大降低开发的难度和成本。
在科研领域,FFmpeg 也发挥着重要作用。研究人员可以利用其先进的编解码算法,探索音视频质量优化的新途径;借助其流媒体传输功能,开展网络带宽利用、视频传输延迟等方面的研究,为提升音视频在不同网络环境下的播放体验提供理论依据和技术支持。
此外,FFmpeg 的开源属性更是为其发展注入了源源不断的活力。全球的开发者们汇聚于此,共同参与项目的开发与维护,不断为其增添新功能、修复漏洞,使得 FFmpeg 始终保持着与时俱进的姿态,适应不断变化的音视频市场需求。
下面为你详细介绍使用 FFmpeg 库在 C++ 里播放 MP3 文件的代码流程,整个流程主要包含初始化、打开文件、查找流信息、查找解码器、打开解码器、重采样、播放音频以及资源释放等步骤。
- 初始化 FFmpeg 库
在使用 FFmpeg 库的功能之前,需要对其进行初始化,主要是注册所有可用的编解码器、格式和协议。
#include <iostream>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
#include <libavutil/avutil.h>
}// 初始化 FFmpeg 库
avformat_network_init();
av_register_all();
- 打开输入文件
使用 avformat_open_input 函数打开 MP3 文件,同时创建 AVFormatContext 结构体来保存文件的格式信息。
AVFormatContext* formatContext = nullptr;
const char* filePath = "example.mp3";
if (avformat_open_input(&formatContext, filePath, nullptr, nullptr) != 0) {std::cerr << "Could not open input file" << std::endl;return -1;
}
- 获取流信息
调用 avformat_find_stream_info 函数获取文件的流信息,例如音频流、视频流等。
if (avformat_find_stream_info(formatContext, nullptr) < 0) {std::cerr << "Could not find stream information" << std::endl;avformat_close_input(&formatContext);return -1;
}
- 查找音频流
通过 av_find_best_stream 函数在文件中查找音频流。
int audioStreamIndex = -1;
AVCodecParameters* codecParameters = nullptr;
for (unsigned int i = 0; i < formatContext->nb_streams; i++) {if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {audioStreamIndex = i;codecParameters = formatContext->streams[i]->codecpar;break;}
}
if (audioStreamIndex == -1) {std::cerr << "Could not find audio stream" << std::endl;avformat_close_input(&formatContext);return -1;
}
- 查找并打开解码器
使用 avcodec_find_decoder 函数查找对应的音频解码器,再用 avcodec_open2 函数打开解码器。
AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id);
if (!codec) {std::cerr << "Could not find codec" << std::endl;avformat_close_input(&formatContext);return -1;
}AVCodecContext* codecContext = avcodec_alloc_context3(codec);
if (!codecContext) {std::cerr << "Could not allocate codec context" << std::endl;avformat_close_input(&formatContext);return -1;
}if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {std::cerr << "Could not copy codec parameters to context" << std::endl;avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;
}if (avcodec_open2(codecContext, codec, nullptr) < 0) {std::cerr << "Could not open codec" << std::endl;avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;
}
- 初始化重采样上下文
由于 MP3 文件的音频格式可能与系统播放设备支持的格式不一致,需要使用 swr_alloc_set_opts 函数进行重采样。
SwrContext* swrContext = swr_alloc_set_opts(nullptr,AV_CH_LAYOUT_STEREO, // 输出声道布局AV_SAMPLE_FMT_S16, // 输出样本格式codecContext->sample_rate, // 输出采样率codecContext->channel_layout, // 输入声道布局codecContext->sample_fmt, // 输入样本格式codecContext->sample_rate, // 输入采样率0, nullptr);if (!swrContext || swr_init(swrContext) < 0) {std::cerr << "Could not initialize resampler" << std::endl;avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;
}
- 读取、解码和重采样音频数据
使用 av_read_frame 函数读取音频数据包,再用 avcodec_send_packet 和 avcodec_receive_frame 函数进行解码,最后使用 swr_convert 函数进行重采样。
AVPacket* packet = av_packet_alloc();
AVFrame* frame = av_frame_alloc();while (av_read_frame(formatContext, packet) >= 0) {if (packet->stream_index == audioStreamIndex) {if (avcodec_send_packet(codecContext, packet) < 0) {std::cerr << "Error sending packet to decoder" << std::endl;continue;}while (avcodec_receive_frame(codecContext, frame) == 0) {// 重采样uint8_t* outputData[1];int outputSamples = swr_convert(swrContext, outputData, frame->nb_samples,(const uint8_t**)frame->data, frame->nb_samples);// 这里需要将重采样后的数据发送到音频设备进行播放// 不同系统的音频播放 API 不同,例如在 Windows 下可以使用 WaveOut 或 WASAPI}}av_packet_unref(packet);
}
- 释放资源
在音频播放结束后,需要释放之前分配的所有资源。
av_frame_free(&frame);
av_packet_free(&packet);
swr_free(&swrContext);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
整个播放 MP3 文件的流程可以概括为:初始化 FFmpeg 库 → 打开输入文件 → 获取流信息 → 查找音频流 → 查找并打开解码器 → 初始化重采样上下文 → 读取、解码和重采样音频数据 → 释放资源。需要注意的是,上述代码中重采样后的数据需要使用相应系统的音频播放 API 发送到音频设备进行播放,不同操作系统的音频播放 API 不同,例如 Windows 下的 WaveOut、WASAPI,Linux 下的 ALSA、PulseAudio 等。
二、效果
三、读取歌词类
#pragma once#include <QList>
#include <QString>// 歌词条目结构体
struct stLyricsEntry {int m_nTime; // 时间,单位为毫秒std::string m_strText; // 歌词文本
};class CLyricsReader {
public:CLyricsReader() = default;bool LoadLrcFile(const QString& strFilePath);std::string GetCurrentLyrics(int nCurrentTime);private:QList<stLyricsEntry> m_lstLyricsEntries; // 歌词条目列表
};#include "../Include/LyricsReader.h"
#include "QtGui/Include/Conversion.h"
#include <QFile>
#include <QTextStream>
#include <QRegularExpression>
#include <QTextCodec>
#include <QDebug>bool CLyricsReader::LoadLrcFile(const QString& strFilePath)
{QFile file(strFilePath);if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {qDebug() << "Failed to open LRC file:" << strFilePath;return false;}QByteArray data = file.readAll();QTextCodec* codec = nullptr;if (QTextCodec::codecForUtfText(data)) {codec = QTextCodec::codecForName("UTF-8");}else {// 尝试其他常见编码,如 GBKcodec = QTextCodec::codecForLocale();}QString text = codec->toUnicode(data);QTextStream in(&text);QRegularExpression timeRegex(R"(\[(\d+):(\d+\.\d+)\])");while (!in.atEnd()) {QString line = in.readLine();QRegularExpressionMatchIterator matchIterator = timeRegex.globalMatch(line);while (matchIterator.hasNext()){QRegularExpressionMatch match = matchIterator.next();int minutes = match.captured(1).toInt();double seconds = match.captured(2).toDouble();int time = static_cast<int>((minutes * 60 + seconds) * 1000);QString text = line.mid(match.capturedEnd());if (!text.isEmpty()) {stLyricsEntry entry;entry.m_nTime = time;entry.m_strText = TransUnicode2String(text);m_lstLyricsEntries.append(entry);}}}file.close();// 按时间排序std::sort(m_lstLyricsEntries.begin(), m_lstLyricsEntries.end(), [](const stLyricsEntry& a, const stLyricsEntry& b) {return a.m_nTime < b.m_nTime;});return true;
}std::string CLyricsReader::GetCurrentLyrics(int nCurrentTime)
{for (int i = m_lstLyricsEntries.size() - 1; i >= 0; --i) {if (m_lstLyricsEntries[i].m_nTime<= nCurrentTime){return m_lstLyricsEntries[i].m_strText;}}return "";
}
三、播放类
#include "../Include/AudioPlayer.h"
#include "QtGui/Include/Conversion.h"#include <QIODevice>
#include <QBuffer>
#include <QDebug>extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswresample/swresample.h>
#include <libavutil/avutil.h>
}CAudioPlayer::CAudioPlayer(QObject* parent): QThread(parent)
{m_eControlType = E_CONTROL_NONE;m_pAudioOutput = nullptr;m_bRun = false;m_nSeekMs = 0;
}CAudioPlayer::~CAudioPlayer()
{}void CAudioPlayer::Quit()
{m_bRun = false;wait();
}void CAudioPlayer::Play(const std::string& strFilePath)
{m_strFilePath = strFilePath;m_eControlType = E_CONTROL_PLAY;if (!isRunning()){m_bRun = true;start();}
}void CAudioPlayer::Stop()
{if (isRunning()){m_eControlType = E_CONTROL_STOP;}
}void CAudioPlayer::Pause()
{if (isRunning()){m_eControlType = E_CONTROL_PAUSE;}
}void CAudioPlayer::Resume()
{if (isRunning()){m_eControlType = E_CONTROL_RESUME;}
}void CAudioPlayer::Seek(int nMs)
{if (isRunning()){m_eControlType = E_CONTROL_SEEK;m_nSeekMs = nMs;}
}void CAudioPlayer::run()
{if (!InitAudioOutput(44100)){qDebug() << "InitAudioOutput failed";return;}while (m_bRun){switch (m_eControlType){case E_CONTROL_NONE:{msleep(20);}break;case E_CONTROL_PLAY:{m_eControlType = E_CONTROL_NONE;RunPlay();}break;default:m_eControlType = E_CONTROL_NONE;break;}}
}bool CAudioPlayer::InitAudioOutput(int nSampleRate)
{if (m_pAudioOutput){return false;}QAudioFormat format;format.setSampleRate(nSampleRate);format.setChannelCount(2);format.setSampleSize(16);format.setCodec("audio/pcm");format.setByteOrder(QAudioFormat::LittleEndian);format.setSampleType(QAudioFormat::SignedInt);QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());if (!info.isFormatSupported(format)){qDebug() << "Raw audio format not supported by backend, cannot play audio.";return false;}m_pAudioOutput = std::make_unique<QAudioOutput>(format);m_pAudioOutput->setBufferSize(100000);return true;
}bool CAudioPlayer::CheckControlType()
{if (!m_pAudioOutput){return false;}bool bRet = false;if (m_eControlType == E_CONTROL_PAUSE){while (m_eControlType == E_CONTROL_PAUSE){m_pAudioOutput->suspend();msleep(20);}if (m_eControlType == E_CONTROL_RESUME){m_pAudioOutput->resume();}}if (m_eControlType == E_CONTROL_PLAY){bRet = true;if (m_pAudioOutput->state() == QAudio::StoppedState){m_pAudioOutput->stop();}}if (m_eControlType == E_CONTROL_STOP){bRet = true;if (m_pAudioOutput->state() == QAudio::ActiveState){m_pAudioOutput->stop();}}return bRet;
}void CAudioPlayer::DebugError(int nError)
{char cError[1024] = { 0 };av_strerror(nError, cError, sizeof(cError));qDebug() << "Error:" << cError;
}void CAudioPlayer::RunPlay()
{int nRet = 0;int nDestMs = 0;int nCurMs = 0;if (!m_pAudioOutput){qDebug() << "m_pAudioOutput is nullptr";return;}av_log_set_level(AV_LOG_ERROR);avformat_network_init();AVFormatContext* pAvFormatContext = nullptr;if ((nRet = avformat_open_input(&pAvFormatContext, m_strFilePath.c_str(), nullptr, nullptr)) != 0){qDebug() << "Could not open input file";return;}AVDictionary* pOpts = nullptr;av_dict_set(&pOpts, "analyzeduration", "2147483647", 0); // 设置最大分析时长av_dict_set(&pOpts, "probesize", "2147483647", 0); // 设置最大探测大小if ((nRet = avformat_find_stream_info(pAvFormatContext, &pOpts)) < 0){qDebug() << "Could not find stream information";av_dict_free(&pOpts);avformat_close_input(&pAvFormatContext);return;}av_dict_free(&pOpts);int nAudioStreamIndex = -1;nAudioStreamIndex = av_find_best_stream(pAvFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);if (nAudioStreamIndex < 0){qDebug() << "Could not find audio stream";avformat_close_input(&pAvFormatContext);return;}qDebug() << "Audio stream index:" << nAudioStreamIndex;AVCodec *pCodec = avcodec_find_decoder(pAvFormatContext->streams[nAudioStreamIndex]->codecpar->codec_id);AVCodecContext* pCodecContext = avcodec_alloc_context3(pCodec);avcodec_parameters_to_context(pCodecContext, pAvFormatContext->streams[nAudioStreamIndex]->codecpar);nRet = avcodec_open2(pCodecContext, pCodec, nullptr);if (nRet < 0){qDebug() << "Could not open codec";avcodec_free_context(&pCodecContext);avformat_close_input(&pAvFormatContext);return;}SwrContext *pSwrContext = nullptr;pSwrContext = swr_alloc_set_opts(nullptr, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, pCodecContext->sample_rate,pCodecContext->channel_layout, pCodecContext->sample_fmt, pCodecContext->sample_rate, 0, nullptr);swr_init(pSwrContext);nDestMs = av_q2d(pAvFormatContext->streams[nAudioStreamIndex]->time_base) * 1000 * pAvFormatContext->streams[nAudioStreamIndex]->duration;qDebug() << TransString2Unicode("码率:") << pCodecContext->bit_rate;qDebug() << TransString2Unicode("格式:") << pCodecContext->sample_fmt;qDebug() << TransString2Unicode("通道:") << pCodecContext->channels;qDebug() << TransString2Unicode("采样率:") << pCodecContext->sample_rate;qDebug() << TransString2Unicode("时长:") << nDestMs;qDebug() << TransString2Unicode("解码器:") << pCodec->name;Q_EMIT SigDuration(nCurMs, nDestMs);AVPacket* pAvPacket = av_packet_alloc();AVFrame* pAvFrame = av_frame_alloc();m_pAudioOutput->stop();QIODevice* pAudioDevice = m_pAudioOutput->start();while (1){if (CheckControlType()){break;}if (m_eControlType == CAudioPlayer::E_CONTROL_SEEK){av_seek_frame(pAvFormatContext, nAudioStreamIndex, m_nSeekMs / (double)1000.0 / av_q2d(pAvFormatContext->streams[nAudioStreamIndex]->time_base), AVSEEK_FLAG_BACKWARD);m_eControlType = E_CONTROL_NONE;Q_EMIT SigSeekOk();}nRet = av_read_frame(pAvFormatContext, pAvPacket);if (nRet == AVERROR_EOF){// 到达文件末尾,向解码器发送刷新信号nRet = avcodec_send_packet(pCodecContext, nullptr);if (nRet < 0){DebugError(nRet);qDebug() << "Could not send flush packet";}while (nRet >= 0){nRet = avcodec_receive_frame(pCodecContext, pAvFrame);if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF){break;}else if (nRet < 0){DebugError(nRet);break;}// 处理剩余的帧uint8_t* pData[2] = { 0 };int nByteCnt = pAvFrame->nb_samples * 2 * 2;std::unique_ptr<uint8_t[]> pDestBuf(new uint8_t[nByteCnt]);pData[0] = pDestBuf.get();nRet = swr_convert(pSwrContext, &pData[0], pAvFrame->nb_samples,(const uint8_t**)(pAvFrame->data), pAvFrame->nb_samples);while (m_pAudioOutput->bytesFree() < nByteCnt){if (CheckControlType()){break;}msleep(10);}if (!CheckControlType()){pAudioDevice->write((const char*)pDestBuf.get(), nByteCnt);}nCurMs = av_q2d(pAvFormatContext->streams[nAudioStreamIndex]->time_base) * 1000 * pAvFrame->pts;Q_EMIT SigDuration(nCurMs, nDestMs);}qDebug() << "End of file reached";SigDuration(nDestMs, nDestMs);break;}else if (nRet < 0){DebugError(nRet);qDebug() << "Could not read frame";SigDuration(nDestMs, nDestMs);av_packet_unref(pAvPacket);break;}if (pAvPacket->stream_index == nAudioStreamIndex){if (CheckControlType()){av_packet_unref(pAvPacket);break;}// 发送 AVPacket 到解码器nRet = avcodec_send_packet(pCodecContext, pAvPacket);av_packet_unref(pAvPacket); // 发送后释放 AVPacketif (nRet < 0){DebugError(nRet);qDebug() << "Could not send packet";continue;}// 从解码器接收 AVFramewhile (true){nRet = avcodec_receive_frame(pCodecContext, pAvFrame);if (nRet == AVERROR(EAGAIN) || nRet == AVERROR_EOF){break;}else if (nRet < 0){DebugError(nRet);break;}uint8_t* pData[2] = { 0 };int nByteCnt = pAvFrame->nb_samples * 2 * 2;std::unique_ptr<uint8_t[]> pDestBuf(new uint8_t[nByteCnt]);pData[0] = pDestBuf.get();nRet = swr_convert(pSwrContext, &pData[0], pAvFrame->nb_samples,(const uint8_t**)(pAvFrame->data), pAvFrame->nb_samples);while (m_pAudioOutput->bytesFree() < nByteCnt){if (CheckControlType()){break;}msleep(10);}if (!CheckControlType()){pAudioDevice->write((const char*)pDestBuf.get(), nByteCnt);}nCurMs = av_q2d(pAvFormatContext->streams[nAudioStreamIndex]->time_base) * 1000 * pAvFrame->pts;Q_EMIT SigDuration(nCurMs, nDestMs);}}else{av_packet_unref(pAvPacket); // 非音频包,释放}}av_frame_free(&pAvFrame);av_packet_free(&pAvPacket);swr_free(&pSwrContext);avcodec_free_context(&pCodecContext);avformat_close_input(&pAvFormatContext);
}
四、主界面类
#include "../Include/EMusicMainWindow.h"
#include "ui_EMusicMainWindow.h"
#include "QtGui/Include/Conversion.h"
#include <QFileDialog>
#include <QThread>
#include <QDebug>CEMusicMainWindow::CEMusicMainWindow(QWidget* parent): QMainWindow(parent), ui(std::make_unique<Ui::CEMusicMainWindow>())
{ui->setupUi(this);InitUI();
}CEMusicMainWindow::~CEMusicMainWindow()
{m_AudioPlayer.Stop();m_AudioPlayer.Quit();
}void CEMusicMainWindow::InitUI()
{m_bIsPlaying = false;m_bSeeking = false;connect(&m_AudioPlayer, &CAudioPlayer::SigDuration, this, &CEMusicMainWindow::SlotUpdateLyricsAndTime);connect(&m_AudioPlayer, &CAudioPlayer::SigSeekOk, this, &CEMusicMainWindow::SlotSeekOk);
}void CEMusicMainWindow::on_pushButton_StopOrPlay_clicked()
{if (m_strMusicFilePath.isEmpty()){return;}if (m_bIsPlaying){m_bIsPlaying = false;m_AudioPlayer.Pause();ui->pushButton_StopOrPlay->setStyleSheet("image: url(:/Play.png);");}else{m_bIsPlaying = true;m_bSeeking = false;ui->pushButton_StopOrPlay->setStyleSheet("image: url(:/Stop.png);");if (m_strOldMusicFilePath.isEmpty()){m_strOldMusicFilePath = m_strMusicFilePath;m_AudioPlayer.Play(TransUnicode2String(m_strMusicFilePath));}else if (m_strMusicFilePath != m_strOldMusicFilePath){m_strOldMusicFilePath = m_strMusicFilePath;m_AudioPlayer.Play(TransUnicode2String(m_strMusicFilePath));}else if(m_AudioPlayer.GetControlType() == CAudioPlayer::E_CONTROL_PAUSE){m_AudioPlayer.Resume();}else {m_strOldMusicFilePath = m_strMusicFilePath;m_AudioPlayer.Play(TransUnicode2String(m_strMusicFilePath));}}
}void CEMusicMainWindow::SlotUpdateLyricsAndTime(int nCurrentTime, int nDestTime)
{std::string strLyrics = m_LyricsReader.GetCurrentLyrics(nCurrentTime);ui->label_Words->setText(TransString2Unicode(strLyrics));if (nCurrentTime == nDestTime && nCurrentTime != 0){m_bIsPlaying = false;ui->pushButton_StopOrPlay->setStyleSheet("image: url(:/Play.png);");}static int currentMs1 = -1, destMs1 = -1;if (currentMs1 == nCurrentTime && destMs1 == nDestTime){return;}currentMs1 = nCurrentTime;destMs1 = nDestTime;//qDebug() << "onDuration:" << nCurrentTime << nDestTime << m_bSeeking;QString currentTime = QString("%1:%2:%3").arg(currentMs1 / 360000 % 60, 2, 10, QChar('0')).arg(currentMs1 / 6000 % 60, 2, 10, QChar('0')).arg(currentMs1 / 1000 % 60, 2, 10, QChar('0'));QString destTime = QString("%1:%2:%3").arg(destMs1 / 360000 % 60, 2, 10, QChar('0')).arg(destMs1 / 6000 % 60, 2, 10, QChar('0')).arg(destMs1 / 1000 % 60, 2, 10, QChar('0'));ui->label_Process->setText(currentTime + "/" + destTime);if (!m_bSeeking) //未滑动{ui->slider_Seek->setMaximum(nDestTime);ui->slider_Seek->setValue(nCurrentTime);}
}void CEMusicMainWindow::SlotSeekOk()
{m_bSeeking = false;
}void CEMusicMainWindow::on_slider_Seek_sliderPressed()
{m_bSeeking = true;
}void CEMusicMainWindow::on_slider_Seek_sliderReleased()
{m_AudioPlayer.Seek(ui->slider_Seek->value());
}void CEMusicMainWindow::on_pushButton_Select_clicked()
{m_strMusicFilePath = QFileDialog::getOpenFileName(this, TransString2Unicode("选择音乐文件"), "", "Music Files (*.mp3)");if (!m_strMusicFilePath.isEmpty()){m_LyricsReader.LoadLrcFile(m_strMusicFilePath.left(m_strMusicFilePath.lastIndexOf('.')) + ".lrc");}on_pushButton_StopOrPlay_clicked();
}
五、后续完善
完成其他功能
相关文章:

QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音…...

算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...

jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...

前端开发者常用网站
Can I use网站:一个查询网页技术兼容性的网站 一个查询网页技术兼容性的网站Can I use:Can I use... Support tables for HTML5, CSS3, etc (查询浏览器对HTML5的支持情况) 权威网站:MDN JavaScript权威网站:JavaScript | MDN...
在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7
在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤: 第一步: 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为: // 改为 v…...

如何在Windows本机安装Python并确保与Python.NET兼容
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
加密通信 + 行为分析:运营商行业安全防御体系重构
在数字经济蓬勃发展的时代,运营商作为信息通信网络的核心枢纽,承载着海量用户数据与关键业务传输,其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级,传统安全防护体系逐渐暴露出局限性&a…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...
Monorepo架构: Nx Cloud 扩展能力与缓存加速
借助 Nx Cloud 实现项目协同与加速构建 1 ) 缓存工作原理分析 在了解了本地缓存和远程缓存之后,我们来探究缓存是如何工作的。以计算文件的哈希串为例,若后续运行任务时文件哈希串未变,系统会直接使用对应的输出和制品文件。 2 …...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...

Matlab实现任意伪彩色图像可视化显示
Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中,如何展示好看的实验结果图像非常重要!!! 1、灰度原始图像 灰度图像每个像素点只有一个数值,代表该点的亮度(或…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
Vue3中的computer和watch
computed的写法 在页面中 <div>{{ calcNumber }}</div>script中 写法1 常用 import { computed, ref } from vue; let price ref(100);const priceAdd () > { //函数方法 price 1price.value ; }//计算属性 let calcNumber computed(() > {return ${p…...

图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...

《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...

AxureRP-Pro-Beta-Setup_114413.exe (6.0.0.2887)
Name:3ddown Serial:FiCGEezgdGoYILo8U/2MFyCWj0jZoJc/sziRRj2/ENvtEq7w1RH97k5MWctqVHA 注册用户名:Axure 序列号:8t3Yk/zu4cX601/seX6wBZgYRVj/lkC2PICCdO4sFKCCLx8mcCnccoylVb40lP...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...

02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...
React从基础入门到高级实战:React 实战项目 - 项目五:微前端与模块化架构
React 实战项目:微前端与模块化架构 欢迎来到 React 开发教程专栏 的第 30 篇!在前 29 篇文章中,我们从 React 的基础概念逐步深入到高级技巧,涵盖了组件设计、状态管理、路由配置、性能优化和企业级应用等核心内容。这一次&…...

uni-app学习笔记三十五--扩展组件的安装和使用
由于内置组件不能满足日常开发需要,uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件,需要安装才能使用。 一、安装扩展插件 安装方法: 1.访问uniapp官方文档组件部分:组件使用的入门教程 | uni-app官网 点击左侧…...

6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...