python:VOC格式數據集轉換為YOLO數據集格式

作者:CSDN @ _養樂多_

本文將介紹如何將目標檢測中常用的VOC格式數據集轉換為YOLO數據集,并進行數據集比例劃分,從而方便的進行YOLO目標檢測。

如果不想分兩步,可以直接看第三節代碼。


文章目錄

      • 一、將VOC格式數據集轉換為YOLO格式數據集
      • 二、YOLO格式數據集劃分(訓練、驗證、測試)
      • 2.1 版本1
      • 2.2 版本2
      • 2.3 版本3
      • 三、一步到位


一、將VOC格式數據集轉換為YOLO格式數據集

執行以下腳本將VOC格式數據集轉換為YOLO格式數據集。
但是需要注意的是:

  1. 轉換之后的數據集只有Images和labels兩個文件。還需要執行第二節中的腳本進行數據集劃分,將總的數據集劃分為訓練、驗證、測試數據集;
  2. 使用的話,需要修改 class_mapping 中類別名和對應標簽,還有VOC數據集路徑、YOLO數據集路徑。
import os
import shutil
import xml.etree.ElementTree as ET# VOC格式數據集路徑
voc_data_path = 'E:\\DataSet\\helmet-VOC'
voc_annotations_path = os.path.join(voc_data_path, 'Annotations')
voc_images_path = os.path.join(voc_data_path, 'JPEGImages')# YOLO格式數據集保存路徑
yolo_data_path = 'E:\\DataSet\\helmet-YOLO'
yolo_images_path = os.path.join(yolo_data_path, 'images')
yolo_labels_path = os.path.join(yolo_data_path, 'labels')# 創建YOLO格式數據集目錄
os.makedirs(yolo_images_path, exist_ok=True)
os.makedirs(yolo_labels_path, exist_ok=True)# 類別映射 (可以根據自己的數據集進行調整)
class_mapping = {'head': 0,'helmet': 1,'person': 2,# 添加更多類別...
}def convert_voc_to_yolo(voc_annotation_file, yolo_label_file):tree = ET.parse(voc_annotation_file)root = tree.getroot()size = root.find('size')width = float(size.find('width').text)height = float(size.find('height').text)with open(yolo_label_file, 'w') as f:for obj in root.findall('object'):cls = obj.find('name').textif cls not in class_mapping:continuecls_id = class_mapping[cls]xmlbox = obj.find('bndbox')xmin = float(xmlbox.find('xmin').text)ymin = float(xmlbox.find('ymin').text)xmax = float(xmlbox.find('xmax').text)ymax = float(xmlbox.find('ymax').text)x_center = (xmin + xmax) / 2.0 / widthy_center = (ymin + ymax) / 2.0 / heightw = (xmax - xmin) / widthh = (ymax - ymin) / heightf.write(f"{cls_id} {x_center} {y_center} {w} {h}\n")# 遍歷VOC數據集的Annotations目錄,進行轉換
for voc_annotation in os.listdir(voc_annotations_path):if voc_annotation.endswith('.xml'):voc_annotation_file = os.path.join(voc_annotations_path, voc_annotation)image_id = os.path.splitext(voc_annotation)[0]voc_image_file = os.path.join(voc_images_path, f"{image_id}.jpg")yolo_label_file = os.path.join(yolo_labels_path, f"{image_id}.txt")yolo_image_file = os.path.join(yolo_images_path, f"{image_id}.jpg")convert_voc_to_yolo(voc_annotation_file, yolo_label_file)if os.path.exists(voc_image_file):shutil.copy(voc_image_file, yolo_image_file)print("轉換完成!")

二、YOLO格式數據集劃分(訓練、驗證、測試)

參考:https://docs.ultralytics.com/datasets/detect/#ultralytics-yolo-format

隨機將數據集按照0.7-0.2-0.1比例劃分為訓練、驗證、測試數據集。
注意,修改代碼中圖片的后綴,如果是.jpg,就把.png修改為.jpg。

最終結果,

在這里插入圖片描述

2.1 版本1

用版本1劃分就行,也可以用版本2,版本3就不用了。版本1和版本2是兩種不同的組織方式都能訓練。版本1是官方的組織方法。

import os
import shutil
import randomdef make_yolo_dataset(images_folder, labels_folder, output_folder, train_ratio=0.8):# 創建目標文件夾images_train_folder = os.path.join(output_folder, 'images/train')images_val_folder = os.path.join(output_folder, 'images/val')labels_train_folder = os.path.join(output_folder, 'labels/train')labels_val_folder = os.path.join(output_folder, 'labels/val')os.makedirs(images_train_folder, exist_ok=True)os.makedirs(images_val_folder, exist_ok=True)os.makedirs(labels_train_folder, exist_ok=True)os.makedirs(labels_val_folder, exist_ok=True)# 獲取圖片和標簽的文件名(不包含擴展名)image_files = [f for f in os.listdir(images_folder) if f.endswith('.jpg')]label_files = [f for f in os.listdir(labels_folder) if f.endswith('.txt')]image_base_names = set(os.path.splitext(f)[0] for f in image_files)label_base_names = set(os.path.splitext(f)[0] for f in label_files)# 找出圖片和標簽都存在的文件名matched_files = list(image_base_names & label_base_names)# 打亂順序并劃分為訓練集和驗證集random.shuffle(matched_files)split_idx = int(len(matched_files) * train_ratio)train_files = matched_files[:split_idx]val_files = matched_files[split_idx:]# 移動文件到對應文件夾for base_name in train_files:img_src = os.path.join(images_folder, f"{base_name}.jpg")lbl_src = os.path.join(labels_folder, f"{base_name}.txt")img_dst = os.path.join(images_train_folder, f"{base_name}.jpg")lbl_dst = os.path.join(labels_train_folder, f"{base_name}.txt")shutil.copyfile(img_src, img_dst)shutil.copyfile(lbl_src, lbl_dst)for base_name in val_files:img_src = os.path.join(images_folder, f"{base_name}.jpg")lbl_src = os.path.join(labels_folder, f"{base_name}.txt")img_dst = os.path.join(images_val_folder, f"{base_name}.jpg")lbl_dst = os.path.join(labels_val_folder, f"{base_name}.txt")shutil.copyfile(img_src, img_dst)shutil.copyfile(lbl_src, lbl_dst)print("數據集劃分完成!")# 使用示例
images_folder = 'path/to/your/images_folder'  # 原始圖片文件夾路徑
labels_folder = 'path/to/your/labels_folder'  # 原始標簽文件夾路徑
output_folder = 'path/to/your/output_folder'  # 存放結果數據集的文件夾路徑
make_yolo_dataset(images_folder, labels_folder, output_folder)

2.2 版本2

import os
import shutil
import random
from math import floor# 創建輸出目錄的函數
def create_dirs(output_dir):images_dir = os.path.join(output_dir, 'images')labels_dir = os.path.join(output_dir, 'labels')for split in ['train', 'val', 'test']:os.makedirs(os.path.join(images_dir, split), exist_ok=True)os.makedirs(os.path.join(labels_dir, split), exist_ok=True)return images_dir, labels_dir# 獲取圖片和對應txt標簽的列表
def get_files(images_path, labels_path):image_files = [f for f in os.listdir(images_path) if f.endswith(('jpg', 'png', 'jpeg'))]label_files = [f for f in os.listdir(labels_path) if f.endswith('.txt')]# 檢查圖片和標簽是否配對paired_files = []for image_file in image_files:base_name = os.path.splitext(image_file)[0]label_file = base_name + '.txt'if label_file in label_files:paired_files.append((image_file, label_file))return paired_files# 將文件按比例劃分并拷貝到相應目錄
def split_and_copy(paired_files, images_path, labels_path, images_dir, labels_dir, train_ratio, val_ratio):random.shuffle(paired_files)  # 隨機打亂total_files = len(paired_files)train_count = floor(total_files * train_ratio)val_count = floor(total_files * val_ratio)test_count = total_files - train_count - val_countsplits = {'train': paired_files[:train_count],'val': paired_files[train_count:train_count + val_count],'test': paired_files[train_count + val_count:]}for split, files in splits.items():for image_file, label_file in files:shutil.copy(os.path.join(images_path, image_file), os.path.join(images_dir, split, image_file))shutil.copy(os.path.join(labels_path, label_file), os.path.join(labels_dir, split, label_file))print(f'{split}: {len(files)} files')# 主函數
def main():# 寫死的路徑images_path = "E:\\DataSet\\LC\\large_coal_blocked_yolo\\totalImages"  # 替換為實際圖片文件夾路徑labels_path = "E:\\DataSet\\LC\\large_coal_blocked_yolo\\totalLabels"  # 替換為實際txt文件夾路徑output_dir = "E:\\DataSet\\LC\\large_coal_blocked_yolo\\output"  # 替換為實際輸出主目錄路徑# 數據劃分比例train_ratio = 0.7val_ratio = 0.3test_ratio = 0# 容差值用于浮點數比較epsilon = 1e-6# 確保比例之和等于1assert abs(train_ratio + val_ratio + test_ratio - 1) < epsilon, "比例之和必須等于1"# 創建目錄images_dir, labels_dir = create_dirs(output_dir)# 獲取文件列表paired_files = get_files(images_path, labels_path)# 進行劃分并拷貝split_and_copy(paired_files, images_path, labels_path, images_dir, labels_dir, train_ratio, val_ratio)# 調用主函數
if __name__ == "__main__":main()

2.3 版本3

import os
import shutil
import random# YOLO格式數據集保存路徑
yolo_images_path1 = 'E:\\DataSet\\helmet-VOC'
yolo_labels_path1 = 'E:\\DataSet\\helmet-YOLO'
yolo_data_path = yolo_labels_path1yolo_images_path = os.path.join(yolo_images_path1, 'JPEGImages')
yolo_labels_path = os.path.join(yolo_labels_path1, 'labels')# 創建劃分后的目錄結構
train_images_path = os.path.join(yolo_data_path, 'train', 'images')
train_labels_path = os.path.join(yolo_data_path, 'train', 'labels')
val_images_path = os.path.join(yolo_data_path, 'val', 'images')
val_labels_path = os.path.join(yolo_data_path, 'val', 'labels')
test_images_path = os.path.join(yolo_data_path, 'test', 'images')
test_labels_path = os.path.join(yolo_data_path, 'test', 'labels')os.makedirs(train_images_path, exist_ok=True)
os.makedirs(train_labels_path, exist_ok=True)
os.makedirs(val_images_path, exist_ok=True)
os.makedirs(val_labels_path, exist_ok=True)
os.makedirs(test_images_path, exist_ok=True)
os.makedirs(test_labels_path, exist_ok=True)# 獲取所有圖片文件名(不包含擴展名)
image_files = [f[:-4] for f in os.listdir(yolo_images_path) if f.endswith('.png')]# 隨機打亂文件順序
random.shuffle(image_files)# 劃分數據集比例
train_ratio = 0.7
val_ratio = 0.2
test_ratio = 0.1train_count = int(train_ratio * len(image_files))
val_count = int(val_ratio * len(image_files))
test_count = len(image_files) - train_count - val_counttrain_files = image_files[:train_count]
val_files = image_files[train_count:train_count + val_count]
test_files = image_files[train_count + val_count:]# 移動文件到相應的目錄
def move_files(files, src_images_path, src_labels_path, dst_images_path, dst_labels_path):for file in files:src_image_file = os.path.join(src_images_path, f"{file}.png")src_label_file = os.path.join(src_labels_path, f"{file}.txt")dst_image_file = os.path.join(dst_images_path, f"{file}.png")dst_label_file = os.path.join(dst_labels_path, f"{file}.txt")if os.path.exists(src_image_file) and os.path.exists(src_label_file):shutil.move(src_image_file, dst_image_file)shutil.move(src_label_file, dst_label_file)# 移動訓練集文件
move_files(train_files, yolo_images_path, yolo_labels_path, train_images_path, train_labels_path)
# 移動驗證集文件
move_files(val_files, yolo_images_path, yolo_labels_path, val_images_path, val_labels_path)
# 移動測試集文件
move_files(test_files, yolo_images_path, yolo_labels_path, test_images_path, test_labels_path)print("數據集劃分完成!")

三、一步到位

如果不想分兩步進行格式轉換,那么以下腳本結合了以上兩步,直接得到最后按比例劃分訓練、驗證、測試的數據集結果。

在這里插入圖片描述

注意:需要修改 voc_data_path ,yolo_data_path ,class_mapping 以及 ‘.png’ 后綴。

import os
import shutil
import random
import xml.etree.ElementTree as ET
from tqdm import tqdm# VOC格式數據集路徑
voc_data_path = 'E:\\DataSet-VOC'
voc_annotations_path = os.path.join(voc_data_path, 'Annotations')
voc_images_path = os.path.join(voc_data_path, 'JPEGImages')# YOLO格式數據集保存路徑
yolo_data_path = 'E:\\DataSet-YOLO'
yolo_images_path = os.path.join(yolo_data_path, 'images')
yolo_labels_path = os.path.join(yolo_data_path, 'labels')# 創建YOLO格式數據集目錄
os.makedirs(yolo_images_path, exist_ok=True)
os.makedirs(yolo_labels_path, exist_ok=True)# 類別映射 (可以根據自己的數據集進行調整)
class_mapping = {'head': 0,'helmet': 1,'person': 2,# 添加更多類別...
}def convert_voc_to_yolo(voc_annotation_file, yolo_label_file):tree = ET.parse(voc_annotation_file)root = tree.getroot()size = root.find('size')width = float(size.find('width').text)height = float(size.find('height').text)with open(yolo_label_file, 'w') as f:for obj in root.findall('object'):cls = obj.find('name').textif cls not in class_mapping:continuecls_id = class_mapping[cls]xmlbox = obj.find('bndbox')xmin = float(xmlbox.find('xmin').text)ymin = float(xmlbox.find('ymin').text)xmax = float(xmlbox.find('xmax').text)ymax = float(xmlbox.find('ymax').text)x_center = (xmin + xmax) / 2.0 / widthy_center = (ymin + ymax) / 2.0 / heightw = (xmax - xmin) / widthh = (ymax - ymin) / heightf.write(f"{cls_id} {x_center} {y_center} {w} {h}\n")# 遍歷VOC數據集的Annotations目錄,進行轉換
print("開始VOC到YOLO格式轉換...")
for voc_annotation in tqdm(os.listdir(voc_annotations_path)):if voc_annotation.endswith('.xml'):voc_annotation_file = os.path.join(voc_annotations_path, voc_annotation)image_id = os.path.splitext(voc_annotation)[0]voc_image_file = os.path.join(voc_images_path, f"{image_id}.png")yolo_label_file = os.path.join(yolo_labels_path, f"{image_id}.txt")yolo_image_file = os.path.join(yolo_images_path, f"{image_id}.png")convert_voc_to_yolo(voc_annotation_file, yolo_label_file)if os.path.exists(voc_image_file):shutil.copy(voc_image_file, yolo_image_file)print("VOC到YOLO格式轉換完成!")# 劃分數據集
train_images_path = os.path.join(yolo_data_path, 'train', 'images')
train_labels_path = os.path.join(yolo_data_path, 'train', 'labels')
val_images_path = os.path.join(yolo_data_path, 'val', 'images')
val_labels_path = os.path.join(yolo_data_path, 'val', 'labels')
test_images_path = os.path.join(yolo_data_path, 'test', 'images')
test_labels_path = os.path.join(yolo_data_path, 'test', 'labels')os.makedirs(train_images_path, exist_ok=True)
os.makedirs(train_labels_path, exist_ok=True)
os.makedirs(val_images_path, exist_ok=True)
os.makedirs(val_labels_path, exist_ok=True)
os.makedirs(test_images_path, exist_ok=True)
os.makedirs(test_labels_path, exist_ok=True)# 獲取所有圖片文件名(不包含擴展名)
image_files = [f[:-4] for f in os.listdir(yolo_images_path) if f.endswith('.png')]# 隨機打亂文件順序
random.shuffle(image_files)# 劃分數據集比例
train_ratio = 0.7
val_ratio = 0.2
test_ratio = 0.1train_count = int(train_ratio * len(image_files))
val_count = int(val_ratio * len(image_files))
test_count = len(image_files) - train_count - val_counttrain_files = image_files[:train_count]
val_files = image_files[train_count:train_count + val_count]
test_files = image_files[train_count + val_count:]# 移動文件到相應的目錄
def move_files(files, src_images_path, src_labels_path, dst_images_path, dst_labels_path):for file in tqdm(files):src_image_file = os.path.join(src_images_path, f"{file}.png")src_label_file = os.path.join(src_labels_path, f"{file}.txt")dst_image_file = os.path.join(dst_images_path, f"{file}.png")dst_label_file = os.path.join(dst_labels_path, f"{file}.txt")if os.path.exists(src_image_file) and os.path.exists(src_label_file):shutil.move(src_image_file, dst_image_file)shutil.move(src_label_file, dst_label_file)# 移動訓練集文件
print("移動訓練集文件...")
move_files(train_files, yolo_images_path, yolo_labels_path, train_images_path, train_labels_path)
# 移動驗證集文件
print("移動驗證集文件...")
move_files(val_files, yolo_images_path, yolo_labels_path, val_images_path, val_labels_path)
# 移動測試集文件
print("移動測試集文件...")
move_files(test_files, yolo_images_path, yolo_labels_path, test_images_path, test_labels_path)print("數據集劃分完成!")# 刪除原始的 images 和 labels 文件夾
shutil.rmtree(yolo_images_path)
shutil.rmtree(yolo_labels_path)print("原始 images 和 labels 文件夾刪除完成!")

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/72127.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/72127.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/72127.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker容器安裝軟件(完整版)

文章目錄 一、安裝Docker1.1 docker 相關的命令1.2 配置鏡像加速 二. 安裝es2.1 創建網絡2.2 拉取鏡像2.3 創建掛載點目錄2.4 部署單點es&#xff0c;創建es容器2.5 編寫elasticsearch.yml2.6 重啟es容器2.7 測試Elasticsearch是否安裝成功 三. 基于Docker安裝Kibana3.1 拉取鏡…

LINUX 指令大全

Linux服務器上有許多常用的命令&#xff0c;可以幫助你管理文件、目錄、進程、網絡和系統配置等。以下是一些常用的Linux命令&#xff1a; 文件和目錄管理 ls&#xff1a;列出當前目錄中的文件和子目錄 bash lspwd&#xff1a;顯示當前工作目錄的路徑 bash pwdcd&#xff1a;切…

燃氣對我們生活的重要性體現在哪里?

燃氣在我們的生活中有 多方面的重要性 &#xff0c;以下是燃氣對我們生活的重要性的詳細說明&#xff1a; 烹飪和熱水供應 &#xff1a; 燃氣是家庭烹飪的主要能源&#xff0c;能夠快速、高效地加熱食物&#xff0c;使家庭聚餐更加便捷和愉快。 燃氣熱水器能夠在短時間內提供…

NetAssist 5.0.14網絡助手基礎使用及自動應答使用方案

以下是NetAssist v5.0.14自動應答功能的詳細使用步驟&#xff1a; 一、基礎準備&#xff1a; 工具下載網址頁面&#xff1a;https://www.cmsoft.cn/resource/102.html 下載安裝好后&#xff0c;根據需要可以創建多個server&#xff0c;雙擊程序圖標運行即可&#xff0c;下面…

node.js-node.js作為服務器,前端使用WebSocket(單個TCP連接上進行全雙工通訊的協議)

1.WebSocket全雙工通信協議 WebSocket是HTML5開始提供的一種單個TCP連接上進行全雙工通訊的協議。讓客戶端和服務器間的數據交互變得簡單&#xff0c;允許服務端向客戶端主動推送數據。瀏覽器和服務器間只需要完成一次握手&#xff0c;兩者間創建持久性的連接&#xff0c;并進行…

java后端開發day31--集合進階(一)-----Collection集合List集合數據結構1

&#xff08;以下內容全部來自上述課程&#xff09; 1.集合體系結構 List系列集合&#xff1a;添加的元素是有序、可重復、有索引。 Set系列集合&#xff1a;添加的元素是無序、不重復、無索引。 2.Collection集合 Collection是單列集合的祖宗接口&#xff08;不可直接創建…

Qt配置OpenGL相機踩的坑

項目根據LearnOpenGL配置Qt的相機&#xff0c;更新view矩陣和project矩陣的位移向量變得很大&#xff0c;我設置的明明相機位置是(0,0,3)&#xff0c;理想的位移向量剛好是相反數(0,0,-3)&#xff0c;對應的view矩陣位置向量可以變成(0,0,1200)…離模型非常遠矩陣模型也看不見&…

【C++設計模式】第十六篇:迭代器模式(Iterator)

注意&#xff1a;復現代碼時&#xff0c;確保 VS2022 使用 C17/20 標準以支持現代特性。 遍歷聚合對象的統一方式 1. 模式定義與用途 核心思想 ?迭代器模式&#xff1a;提供一種方法順序訪問聚合對象的元素&#xff0c;而無需暴露其內部表示。關鍵用途&#xff1a; 1.?統一…

關于WPS的Excel點擊單元格打開別的文檔的兩種方法的探究【為單元格添加超鏈接】

問題需求 目錄和文件結構如下&#xff1a; E:\Dir_Level1 │ Level1.txt │ └─Dir_Level2│ Level2.txt│ master.xlsx│└─Dir_Level3Level3.txt現在要在master.xlsx點擊單元格進而訪問Level1.txt、Level2.txt、Level3.txt這些文件。 方法一&#xff1a;“單元格右鍵…

聚類中的相似矩陣和拉普拉斯矩陣

前言&#xff08;可以略過&#xff09; 最近在看的是關于聚類的論文&#xff0c;之前對聚類的步驟和相關內容不太了解&#xff0c;為了讀懂論文就去學習了一下&#xff0c;這里將自己的理解記錄下來。學習的不全面&#xff0c;主要是為了看懂論文&#xff0c;后續如果有涉及到聚…

前端筆記 --- vue框架

目錄 基礎知識 指令的修飾符 計算屬性 watch偵聽器的寫法 Vue的生命周期 工程化開發&腳手架 VUE CLI 組件注冊的方式 scoped樣式沖突與原理 data 組件之間的關系和組件通信 v-model詳解 sync修飾符 Dom介紹 操作HTML標簽 總結 ref 和 $refs $nextTick 自…

智能雙劍合璧:基于語音識別與大模型的技術沙龍筆記整理實戰

智能雙劍合璧&#xff1a;基于語音識別與大模型的技術沙龍筆記整理實戰 ——記一次網絡安全技術沙龍的高效知識沉淀 引言&#xff1a;當網絡安全遇上AI生產力工具 在綠盟科技舉辦的"AI驅動的未來網絡安全"內部技術沙龍中&#xff0c;筆者親歷了一場關于網絡安全攻…

數據結構(藍橋杯常考點)

數據結構 前言&#xff1a;這個是針對于藍橋杯競賽常考的數據結構內容&#xff0c;基礎算法比如高精度這些會在下期給大家總結 數據結構 競賽中&#xff0c;時間復雜度不能超過10的7次方&#xff08;1秒&#xff09;到10的8次方&#xff08;2秒&#xff09; 空間限制&#x…

使用 UNIX 命令在設計中搜索標識符:vcsfind 的入門指南

在現代軟件開發和硬件設計中&#xff0c;快速準確地定位和搜索特定標識符是提高開發效率的關鍵。本文將介紹如何使用 UNIX 命令 vcsfind 在設計中搜索標識符&#xff0c;幫助您更高效地管理您的項目。 什么是 vcsfind&#xff1f; vcsfind 是一個強大的 UNIX 命令行工具&#x…

第56天:Web攻防-SQL注入增刪改查盲注延時布爾報錯有無回顯錯誤處理審計復盤

#知識點 1、Web攻防-SQL注入-操作方法&增刪改查 2、Web攻防-SQL注入-布爾&延時&報錯&盲注 一、增刪改查 1、功能&#xff1a;數據查詢 查詢&#xff1a;SELECT * FROM news where id$id 2、功能&#xff1a;新增用戶&#xff0c;添加新聞等 增加&#xff1a;IN…

跳表實現學習

1.介紹 2.源碼 跳表節點&#xff1a; /* ZSETs use a specialized version of Skiplists */ /*** brief 定義跳躍表節點的數據結構。* * 該結構體用于表示跳躍表中的一個節點&#xff0c;包含元素、分數、后向指針和多層鏈表信息。*/ typedef struct zskiplistNode {sds ele;…

Python:正則表達式

正則表達式的基礎和應用 一、正則表達式核心語法&#xff08;四大基石&#xff09; 1. ?元字符&#xff08;特殊符號&#xff09;? ?定位符 ^&#xff1a;匹配字符串開始位置 $&#xff1a;匹配字符串結束位置 \b&#xff1a;匹配單詞邊界?&#xff08;如 \bword\b 匹配…

EB-Cable許可管理中的數據安全與隱私保護

在數字化時代&#xff0c;數據安全與隱私保護已成為企業關注的重中之重。作為專業的電纜管理軟件&#xff0c;EB-Cable許可管理不僅在功能豐富和操作便捷方面表現出色&#xff0c;更在數據安全與隱私保護方面為用戶提供了堅實的保障。本文將詳細介紹EB-Cable許可管理在數據安全…

串口通信函數匯總-ing

謝謝各位佬的閱讀&#xff0c;本文是我自己的理解&#xff0c;如果您發現錯誤&#xff0c;麻煩請您指出&#xff0c;謝謝 首先談談我自己對于串口的理解&#xff0c;隨便拿一個嵌入式的板子&#xff0c;它上面有兩個引腳&#xff0c;一個是rx&#xff0c;一個是tx&#xff0c;r…

如何用HTML5 Canvas實現電子簽名功能??

&#x1f916; 作者簡介&#xff1a;水煮白菜王&#xff0c;一位前端勸退師 &#x1f47b; &#x1f440; 文章專欄&#xff1a; 前端專欄 &#xff0c;記錄一下平時在博客寫作中&#xff0c;總結出的一些開發技巧和知識歸納總結?。 感謝支持&#x1f495;&#x1f495;&#…