ffmpeg+Python实现B站MP4格式音频与视频的合并
安装
官网下载
http://ffmpeg.org/

选择需要的版本

在这个网址下载ffmpeg,https://github.com/BtbN/FFmpeg-Builds/releases

将解压后得到的以下几个文件放置在E:\FFmpeg下

环境变量
此电脑--属性--高级系统设置--环境变量
在系统变量(也就是下面那一半)处找到新建,按如下所示的方法填写

再将%FFMPEG_HOME%以及%FFMPEG_HOME%\bin写入系统变量的Path中
然后一路确定即可
验证
win+R,cmd
输入ffmpeg -version

ffmpeg的使用
对于我将B站PC端缓存的音频mp4和视频mp4文件合并的需求,需要用到的命令为:
ffmpeg.exe -i audio1.mp4 -i video.mp4 -acodec copy -vcodec copy output.mp4
可以把mp4的文件设置成绝对路径,这样就可以转换指定路径的文件以及保存到指定路径了,比如这样:
ffmpeg.exe -i "E:\哔哩哔哩视频\ss27993\77413703\1\audio1.mp4" -i "E:\哔哩哔哩视频\ss27993\77413703\1\video.mp4" -acodec copy -vcodec copy "E:\B站导出视频\Dr.STONE石纪元\第22话宝物.mp4
通过PC端缓存的未合并的视频和音频,全都是命名为video.mp4和audio.mp4
PS:有些兄弟是导出的手机端缓存的视频和音频,是m4s格式的,方法也一样
但光有这条命令还不够,需要自己手动一个个操作,太麻烦了
因此我还需要使用Python来自动帮我完成工作
Python实现自动处理
虽然Python可以实现自动化,减少时间的浪费,但最快的还是以后记得缓存时勾选自动合并

文件结构
PC端缓存的视频保存的文件结构有很多种,我只是根据我碰到的情况写的,但大同小异,修改起来也不麻烦,只是再加个if和else罢了
番剧缓存结构
缓存的番剧是这种结构:



上面举的例子是Dr.stone石纪元,我缓存的鬼灭之刃也是如此
特点是在一个以视频ID号名称的文件夹(ss27993)后,跟着许多子文件夹(57983089等),然后在这些子文件夹中又有一个或多个子文件夹(比如1),然后缓存的视频保存在这个文件夹里,里面有一个info文件(就是json格式),还有audio1.mp4和video.mp4。
PS:
还有一个xml文件,是弹幕信息,暂时我不知道怎么处理

常规缓存结构
除了番剧,一般的视频缓存的结构是这样的


不难看出,这比番剧要少一个层级
文件信息
文件信息主要由info文件和dvi文件来记录
这两种文件都可以直接以json文件来处理,也就是,首先open函数打开文件,然后用json.load转成字典。。。
然后我还发现了一个特点是,视频和音频所在的目录下是info文件,而它的上一层目录下是dvi文件
虽然文件格式基本是一致的,但是里面的键-值关系却不一致,单集视频的名称在番剧中对应的是键Description,在其他视频中对应的是PartName
视频总的名称保存在外层的目录下的info文件或dvi文件中,番剧中对应的键是SeasonTitle,在其他视频中对应的是Title

具体问题具体分析,首先由于我实践得较少,这样的总结不一定对,然后以后也许也会有新的格式、新的变化
代码
具体代码
以下是我的Python代码,你可以先试试能不能用,用不了的话,可以在理解的基础上修改。理解不了的话可以看我的后面的解释,以及代码中的注释,对于运行过程中一些变量的值,我都把它放在注释中了,方便你理解。
# -*- coding = utf-8 -*-
# @time:2020/10/17/017 23:09
# Author:cyx
# @File:main.py.py
# @Software:PyCharm
# 从.info文件中获得了Title信息,但是如果其中有某些特殊字符,保存时可能出现问题
def get_correct_title(title):
error_set = ['/', '\\', ':', '*', '?', '"', '|', '<', '>', '\b', ' ', '.']
correct_title = title
# print(title)
for c in correct_title:
if c in error_set:
correct_title = correct_title.replace(c, '')
return correct_title
def popen(cmd):
# https://blog.csdn.net/qq_41451161/article/details/82901235
try:
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE)
popen.wait()
lines = popen.stdout.readlines()
return [line.decode('gbk') for line in lines]
except BaseException as e:
return -1
if __name__ == '__main__':
import os
import json
import subprocess
# ffmpeg -i video.m4s -i audio.m4s -c:v copy -c:a aac -strict experimental output.mp4
# ffmpeg.exe -i audio1.mp4 -i video.mp4 -acodec copy -vcodec copy output.mp4
AVhao = input("请输入视频AV号:")
superPath = "E:\\哔哩哔哩视频" + "\\" + AVhao
partDirs = [] # 保存每P视频所在的文件夹路径
paths = os.listdir(superPath) # 获取当前路径下所有的文件(包括文件夹)名称
# paths = ['8','9']
# 有时候,会莫名其妙的少了几个视频,可以通过重载来重新加载缺失的视频
# print(paths)
# paths
# ['27993.info', '57983089', '58612211', '59811008', '60862133', '61898240', '62925012', '64005445', '65020725', '66013155', '66808912', '67587875', '68398229', '69175748', '70021307', '70873680', '71617211', '73379440', '74051851', '74974157', '75746600', '76619409', '77413703', '78266594', '79070874', 'cover.jpg', 'desktop.ini']
# 获取每P视频所在的文件夹路径
savePos = ''
seq = []
# 鉴于有些up主命名时毫无规律,导出后无法正常排序,只能手动排序了
# 根据AV号名文件夹下的子文件夹的名称进行排序,但是番剧的话不是这样排序,不过番剧单集的名称很规范,不需要这样
for p in paths:
if '.' not in p:
seq.append(p)
if 'info' in p:
# print(p)
# p:
# 27993.info
info = superPath + "\\" + p
with open(info, 'r', encoding='utf-8') as load_f:
load_dict = json.load(load_f)
projectTitle = load_dict['SeasonTitle']
projectTitle = get_correct_title(projectTitle)
savePos = 'E:\\B站导出视频\\' + projectTitle
print('savePos: ', savePos)
if 'dvi' in p:
# print(p)
# P:
# 328738595.dvi
dvi = superPath + "\\" + p
with open(dvi, 'r', encoding='utf-8') as load_f:
load_dict = json.load(load_f)
projectTitle = load_dict['Title']
projectTitle = get_correct_title(projectTitle)
savePos = 'E:\\B站导出视频\\' + projectTitle
print('savePos: ', savePos)
# 防止文件存在时再次生成该文件夹出现错误
try:
os.mkdir(savePos)
break
except:
pass
subDir = superPath + "\\" + p
if os.path.isdir(subDir):
# print(subDir)
# 所有子文件夹的路径保存在partDirs中
partDirs.append(subDir)
# print("partDirs: ",partDirs)
# partDirs: ['E:\\哔哩哔哩视频\\ss27993\\57983089', 'E:\\哔哩哔哩视频\\ss27993\\58612211', 'E:\\哔哩哔哩视频\\ss27993\\59811008', 'E:\\哔哩哔哩视频\\ss27993\\60862133', 'E:\\哔哩哔哩视频\\ss27993\\61898240', 'E:\\哔哩哔哩视频\\ss27993\\62925012', 'E:\\哔哩哔哩视频\\ss27993\\64005445', 'E:\\哔哩哔哩视频\\ss27993\\65020725', 'E:\\哔哩哔哩视频\\ss27993\\66013155', 'E:\\哔哩哔哩视频\\ss27993\\66808912', 'E:\\哔哩哔哩视频\\ss27993\\67587875', 'E:\\哔哩哔哩视频\\ss27993\\68398229', 'E:\\哔哩哔哩视频\\ss27993\\69175748', 'E:\\哔哩哔哩视频\\ss27993\\70021307', 'E:\\哔哩哔哩视频\\ss27993\\70873680', 'E:\\哔哩哔哩视频\\ss27993\\71617211', 'E:\\哔哩哔哩视频\\ss27993\\73379440', 'E:\\哔哩哔哩视频\\ss27993\\74051851', 'E:\\哔哩哔哩视频\\ss27993\\74974157', 'E:\\哔哩哔哩视频\\ss27993\\75746600', 'E:\\哔哩哔哩视频\\ss27993\\76619409', 'E:\\哔哩哔哩视频\\ss27993\\77413703', 'E:\\哔哩哔哩视频\\ss27993\\78266594', 'E:\\哔哩哔哩视频\\ss27993\\79070874']
videoPos = ''
i = 0
for p in partDirs:
# print(p)
# 列出子文件夹中的所有文件
sublist = os.listdir(p)
# 检查info文件是否在当前子文件夹中
for file in sublist:
# print(file)
# file:
# 1
# 57983089.
# dvi
# cover.jpg
# desktop.ini
if 'info' in file:
infoPos = p + "\\" + file
videoPos = p
else:
subsubDir = p + "\\" + file
if os.path.isdir(subsubDir):
# print(subsubDir)
# subsubDir: E:\哔哩哔哩视频\ss27993\57983089\1
subsubList = os.listdir(subsubDir)
for subsubFile in subsubList:
if 'info' in subsubFile:
infoPos = subsubDir + "\\" + subsubFile
videoPos = subsubDir
break
with open(infoPos, 'r', encoding='utf-8') as load_f:
load_dict = json.load(load_f)
if 'ss' in AVhao:
videoTitle = load_dict['Description']
else:
videoTitle = load_dict['PartName']
videoTitle = get_correct_title(videoTitle)
print('videoTitle: ', videoTitle)
videoDir = videoPos + "\\" + 'video.mp4'
audioDir = videoPos + "\\" + 'audio1.mp4'
# print('videoDir: ', videoDir)
# print('audioDir: ', audioDir)
# videoDir: E:\哔哩哔哩视频\ss27993\74051851\1\video.mp4
# audioDir: E:\哔哩哔哩视频\ss27993\74051851\1\audio1.mp4
if 'ss' in AVhao:
outDir = savePos + "\\" + videoTitle + '.mp4'
else:
outDir = savePos + "\\" + seq[i] + '_' + videoTitle + '.mp4'
i += 1
# 对于那些命名很规范的视频,可以不用自己再排序,进行一下重载,不规范的视频再把这句注释掉就好
outDir = savePos + "\\" + videoTitle + '.mp4'
# command = 'cd ' + superPath + '\\64 && ' # && 多名命令
# command = 'cd ' + 'E:\\ProgramFiles\\ffmpeg' + ' && '
command = 'E:\\FFmpeg\\bin\\ffmpeg.exe -i ' + '"' + audioDir + '"' ' -i ' + '"' + videoDir + '"'+ ' -acodec copy -vcodec copy ' + '"' + outDir + '"'
# print("保存地址",outDir)
# 保存地址 E:\B站导出视频\[Lynda视频]音频录制录音技巧教程(中英双语字幕)全集130课时AudioRecordingTechniques混音录音棚音乐工作室歌曲调音\98录制独奏萨克斯演奏技巧二.mp4
# print(command)
# command = 'E:\\FFmpeg\\bin\\ffmpeg.exe -i "E:\哔哩哔哩视频\ss27993\77413703\1\audio1.mp4" -i "E:\哔哩哔哩视频\ss27993\77413703\1\video.mp4" -acodec copy -vcodec copy "E:\B站导出视频\Dr.STONE石纪元\第22话宝物.mp4'
# os.system(command)
popen(command)
# ffmpeg.exe -i audio1.mp4 -i video.mp4 -acodec copy -vcodec copy output.mp4
break
代码说明
如你所见我的编程水平不高,模块化做的很差,不便于理解,所以有必要进行说明。
直接从main开始看起吧。
AVhao = input("请输入视频AV号:")
superPath = "E:\\哔哩哔哩视频" + "\\" + AVhao
partDirs = [] # 保存每P视频所在的文件夹路径
paths = os.listdir(superPath) # 获取当前路径下所有的文件(包括文件夹)名称
首先是用input接收AV号或BV号的输入,放入AVhao变量中。
然后用superPath变量存放你需要合并的视频的根目录。比如我在B站缓存的所有视频存放在E:\哔哩哔哩视频下,注意程序中要有两条\,然后superPath就是E:\哔哩哔哩视频\AVhao。
os.listdir(),括号中的参数必须是一个真实的路径,这个函数可以得到这个路径下所有的文件和文件夹的名称。
我用paths来存放E:\哔哩哔哩视频\AVhao路径下所有的文件名称和文件夹名称。
为了防止我的表达能力有限带来的理解上的不便,你可以看图,paths对应的是下图中的内容:

这个变量的类型是列表,所以可以用for循环来遍历。
savePos = ''
seq = []
for p in paths:
if '.' not in p:
seq.append(p)
if 'info' in p:
# print(p)
# p:
# 27993.info
info = superPath + "\\" + p
with open(info, 'r', encoding='utf-8') as load_f:
load_dict = json.load(load_f)
projectTitle = load_dict['SeasonTitle']
projectTitle = get_correct_title(projectTitle)
savePos = 'E:\\B站导出视频\\' + projectTitle
print('savePos: ', savePos)
if 'dvi' in p:
# print(p)
# P:
# 328738595.dvi
dvi = superPath + "\\" + p
with open(dvi, 'r', encoding='utf-8') as load_f:
load_dict = json.load(load_f)
projectTitle = load_dict['Title']
projectTitle = get_correct_title(projectTitle)
savePos = 'E:\\B站导出视频\\' + projectTitle
print('savePos: ', savePos)
# 防止文件存在时再次生成该文件夹出现错误
try:
os.mkdir(savePos)
break
except:
pass
subDir = superPath + "\\" + p
if os.path.isdir(subDir):
# print(subDir)
# 所有子文件夹的路径保存在partDirs中
partDirs.append(subDir)
我设置了一个savePos变量,用来表示合并后的视频保存的位置,因为我希望将视频保存在一个我自己指定的文件夹下,同时这个文件夹的名称是这个视频的名称,比如Dr.stone石纪元。
因为想自动化操作,所以我通过缓存文件夹中的info文件和dvi文件来找到视频的名称。
使用open函数打开这两个文件中的一个,因为不确定文件结构是什么样的,所以我用了两个if语句。
然后再用json.load函数将其加载为字典,并根据对应的键读取对应的值,从而可以拼接处对应的保存地址savePos。
由于创建已经存在的同名文件夹会发生错误,为避免这种可能,我将创建目录的操作放在了try语句下。
创建目录用的是os.mkdir()函数,括号中是一个绝对路径。
然后我用subDir来表示子文件夹的名称,注意!是子文件夹,而不是文件。
我用os.path.isdir(subDir)来进行判断,如果是文件夹而不是文件的话,就加到partDirs列表中,partDirs.append(subDir)
这个列表中的每个元素都是一个子文件夹的绝对路径,比如:E:\哔哩哔哩视频\ss27993\57983089
而这个名为seq的显得很突兀,这个其实我也是后来加的,这个列表的作用在于记录当前子文件夹的名称,也就是在AVhao文件夹的下一层,如果不是番剧的话,应当有许多个文件夹分别是1、2、3等等,这些其实对应的是播放列表中的顺序。


而之所以使用if '.' not in p,因为这一层的文件夹全都是用来表示播放顺序的,因此不存在后缀名,从而也就没有“.”。
videoPos = ''
i = 0
for p in partDirs:
# print(p)
# 列出子文件夹中的所有文件
sublist = os.listdir(p)
# 检查info文件是否在当前子文件夹中
for file in sublist:
# print(file)
# file:
# 1
# 57983089.
# dvi
# cover.jpg
# desktop.ini
if 'info' in file:
infoPos = p + "\\" + file
videoPos = p
else:
subsubDir = p + "\\" + file
if os.path.isdir(subsubDir):
# print(subsubDir)
# subsubDir: E:\哔哩哔哩视频\ss27993\57983089\1
subsubList = os.listdir(subsubDir)
for subsubFile in subsubList:
if 'info' in subsubFile:
infoPos = subsubDir + "\\" + subsubFile
videoPos = subsubDir
break
with open(infoPos, 'r', encoding='utf-8') as load_f:
load_dict = json.load(load_f)
if 'ss' in AVhao:
videoTitle = load_dict['Description']
else:
videoTitle = load_dict['PartName']
videoTitle = get_correct_title(videoTitle)
print('videoTitle: ', videoTitle)
videoDir = videoPos + "\\" + 'video.mp4'
audioDir = videoPos + "\\" + 'audio1.mp4'
# print('videoDir: ', videoDir)
# print('audioDir: ', audioDir)
# videoDir: E:\哔哩哔哩视频\ss27993\74051851\1\video.mp4
# audioDir: E:\哔哩哔哩视频\ss27993\74051851\1\audio1.mp4
if 'ss' in AVhao:
outDir = savePos + "\\" + videoTitle + '.mp4'
else:
outDir = savePos + "\\" + seq[i] + '_' + videoTitle + '.mp4'
i += 1
# 对于那些命名很规范的视频,可以不用自己再排序,进行一下重载,不规范的视频再把这句注释掉就好
outDir = savePos + "\\" + videoTitle + '.mp4'
# command = 'cd ' + superPath + '\\64 && ' # && 多名命令
# command = 'cd ' + 'E:\\ProgramFiles\\ffmpeg' + ' && '
command = 'E:\\FFmpeg\\bin\\ffmpeg.exe -i ' + '"' + audioDir + '"' ' -i ' + '"' + videoDir + '"'+ ' -acodec copy -vcodec copy ' + '"' + outDir + '"'
# print("保存地址",outDir)
# 保存地址 E:\B站导出视频\[Lynda视频]音频录制录音技巧教程(中英双语字幕)全集130课时AudioRecordingTechniques混音录音棚音乐工作室歌曲调音\98录制独奏萨克斯演奏技巧二.mp4
# print(command)
# command = 'E:\\FFmpeg\\bin\\ffmpeg.exe -i "E:\哔哩哔哩视频\ss27993\77413703\1\audio1.mp4" -i "E:\哔哩哔哩视频\ss27993\77413703\1\video.mp4" -acodec copy -vcodec copy "E:\B站导出视频\Dr.STONE石纪元\第22话宝物.mp4'
# os.system(command)
popen(command)
# ffmpeg.exe -i audio1.mp4 -i video.mp4 -acodec copy -vcodec copy output.mp4
break
这一部分是我用来实现合并特定路径的视频和音频,并最终导出到指定目录的。
videoPos是待合并的视频的路径,音频文件也在这一目录下。
for p in partDirs:,在这个for循环中遍历的是partDirs列表,这个列表由前面的步骤得到,其中的每个元素都是一个路径。
接下来的这个if-else是用来区别番剧和普通视频合集的,因为它们有不同的目录结构。
infoPos用来记录包含单集视频名称的info文件的路径,然后用open函数打开这个info文件,根据AVhao中是否有ss,判断是否是番剧,如果有ss,则表明是番剧,对应的视频名称为Description键对应的值。否则的话,对应的视频名称为PartName键对应的值,但是如果是要保存为文件的话,当然不能直接以这个名称命名,否则极有可能发生错误,因此我用了一个get_correct_title函数来对标题进行重载,以确保格式正确。
videoDir和audioDir分别是待合并的视频和音频的绝对路径。
然后我根据AVhao中是否有ss,来判断是否是番剧。因为番剧的单集视频名称通常会有序号,所以我可以将输出视频的保存路径设置为保存目录加视频名.mp4。
许多up主的视频名称没有体现视频的先后顺序,这样带来的问题是导出后顺序播放时产生跳集现象。因此我用seq加下划线的方式来为视频排序。比如“1_简介.mp4”。
而对于视频名称本身有排序的情况1_1简介.mp4,这样有些奇怪,所以我们碰到这种情况时,可以直接在下面添加一个outDir = savePos + "\\" + videoTitle + '.mp4',其他情况不用时注释掉就好。
最后是用Python程序执行Dos命令,我将命令设为command变量,通过for循环,会自动生成不同的命令,然后执行命令行有两种方法,一种是导入os库,使用os.system(command),另一种是我在https://blog.csdn.net/qq_41451161/article/details/82901235借用的popen函数,这个也可以用,但需要导入subprocess库。
使用命令行上,有两个比较坑的地方,一是前面必须给出ffmpeg.exe的绝对路径,也就是E:\FFmpeg\bin\ffmpeg.exe,在Python中用是这样的,但直接在命令行中,只要输入ffmpeg.exe即可(前提是你设置好了环境变量)。第二个坑是,command赋值时,一定给路径加上引号,否则的话识别命令时会发生错误。
ffmpeg+Python实现B站MP4格式音频与视频的合并的更多相关文章
- Python文件操作——读写图片,音频,视频
注意:其实就是将mode="rb"或者mode="wb",因为图片,视频,音频就是二进制进行读取,b 代表binary ,其他的和一般文件操作步骤一样,另外, ...
- 玩转音频、视频的利器:FFmpeg
导语 当下直播平台发展十分迅猛,依靠游戏内直播平台的发展带动游戏活跃提升收入,那么对于我们开发来说如何玩转视频呢?下面就来介绍一个音频.视频处理利器——FFmpeg. FFmpeg 简介 FFmpeg ...
- 记MacOs视频mov与mp4格式转换问题解决
综述 记录了mov转mp4格式的方法 记录了自己是多蠢 问题背景 这学期选修的<工程英语视听说>课,需要提交一段口语考试视频,于是乎: 带着我的大疆Mavic Mini 和iPad Pro ...
- Android 音视频开发(一):PCM 格式音频的播放与采集
什么是 PCM 格式 声音从模拟信号转化为数字信号的技术,经过采样.量化.编码三个过程将模拟信号数字化. 采样 顾名思义,对模拟信号采集样本,该过程是从时间上对信号进行数字化,例如每秒采集 44100 ...
- 视频下载四大神器—如何下载优酷/爱奇艺/腾讯/B站超清无水印视频
视频下载四大神器—如何下载优酷/爱奇艺/腾讯/B站超清无水印视频 2018-07-11 | 标签»下载, 下载工具, 视频 又是视频下载,老生常谈的话题.阿刚同学已在乐软博客多次与大家分享推荐 ...
- python采集A站m3u8视频格式视频
基本开发环境 (https://jq.qq.com/?_wv=1027&k=NofUEYzs) Python 3.6 Pycharm 相关模块的使用 (https://jq.qq.com/?_ ...
- 通过ffmpeg转换为mp4格式
FFMPEG -i example.wmv -c:v libx264 -strict -2 output.mp4FFMPEG -i example.wmv -c:v libx264 -stri ...
- 多媒体文件格式(一):MP4 格式
在互联网常见的格式中,跨平台最好的应该就属MP4文件了.因为MP4文件既可以在PC平台的Flashplayer中播放,又可以在移动平台的Android.iOS等平台中进行播放,而且使用系统默认的播放器 ...
- Ubuntu下编译opencv 和Ubuntu使用ffmpeg实现音频、视频的抽取
一.使用Ubuntu编译opencv (前提是Ubuntu内已经正确配置了opencv,个人采用opencv3.2) g++ 1.cpp -o 1 `pkg-config --cflags --lib ...
随机推荐
- Redis基础知识补充及持久化、备份介绍
Redis知识补充 在上一篇博客<Redis基础认识及常用命令使用(一)–技术流ken>中已经介绍了redis的一些基础知识,以及常用命令的使用,本篇博客将补充一些基础知识以及redis持 ...
- C#开发PACS医学影像处理系统(十九):Dicom影像放大镜
在XAML代码设计器中,添加canvas画布与圆形几何对象,利用VisualBrush笔刷来复制画面内容到指定容器: <Canvas x:Name="CvsGlass" Wi ...
- Win10使用VMWare15安装Ubuntu-18.04.2-desktop-amd64
本文在Win10系统中使用VMWare Workstation Pro 15.1.0虚拟机安装Ubuntu-18.04.2-desktop-amd64.iso系统,同时安装VMWare Tools(实 ...
- Java锁?分布式锁?乐观锁?行锁?
转载自:公众号来源:码农翻身 作者:刘欣 Tomcat的锁 Tomcat是这个系统的核心组成部分, 每当有用户请求过来,Tomcat就会从线程池里找个线程来处理,有的执行登录,有的查看购物车,有的下订 ...
- C语言实现数据结构的邻接矩阵----数组生成矩阵、打印、深度优先遍历和广度优先遍历
写在前面 图的存储结构有两种:一种是基于二维数组的邻接矩阵表示法. 另一种是基于链表的的邻接表表示法. 在邻接矩阵中,可以如下表示顶点和边连接关系: 说明: 将顶点对应为下标,根据横纵坐标将矩阵中的某 ...
- 实战:一种在http请求中使用protobuffer+nginx+lua收集打点日志的方案
背景 app打点日志的上报和收集,是互联网公司的基本需求. 一.方案选择 1.1 protobuffer vs json 探究一种以最高效的方式上报和解析打点数据是一个系统性的问题,需要解决的子问题有 ...
- lamda表达式与Stream 流操作,reduce,flatMap,groupingBy等
/** * 符合lambda表达式的接口也叫函数式接口: * 除了默认方法和Object类的方法外,只有一个抽象方法的接口才能符合lambda表达式的要求 * 可以使用@FunctionalInter ...
- 可变参数__VA_ARGS__使用
1. 调试功能一般会使用到宏+可变参数的方式 1.1 ##__VA_ARGS__ 之详细解析 例如: case A. #define my_print1(...) printf(__V ...
- P2652 同花顺
P2652 同花顺 Link 题目背景 所谓同花顺,就是指一些扑克牌,它们花色相同,并且数字连续. 题目描述 现在我手里有 \(n\) 张扑克牌,但它们可能并不能凑成同花顺.我现在想知道,最少更换其中 ...
- farbic-sdk-java 学习部署
准备工作 1.fabric基础网络环境 2.环境准备(jdk环境.maven环境) 3.启动fabric测试网络 4.在idea中测试java-sdk 1.fabric环境准备 1.fabric基础环 ...