【iOS】数据持久化(三)之SQLite3数据库

news2024/11/26 20:33:08

目录

    • 数据库简介
    • 什么是`SQLite`?
      • 在Xcode引入SQLite API
      • SQL语句的种类
      • 存储字段类型
    • SQLite的使用
      • 创建数据库
      • 创建表和删表
      • 数据表操作
        • 增(插入数据INSERT)
        • 删(删除数据DELETE)
        • 改(更新数据UPDATE)
        • 查(查询数据SELECT)
      • 补充
        • 条件语句
        • 起别名
        • 计算数据的数量
        • 排序
        • LIMIT
    • 结束语

数据库简介

本文介绍SQLite的使用,首先简单了解一下什么是数据库

  • 数据库(Database)是根据数据结构来组织、存储和管理数据的仓库
  • 数据库大致分为两种:关系型数据库(主流)、对象型数据库

什么是SQLite

SQLite是一款轻型的嵌入式数据库(用C语言写的开源库),实现了一个自包含的SQL关系型数据库引擎,可存储并操作大量数据,作为关系型数据库我们可以在一个数据库中建立多张相关联的表来解决大量数据重复的问题,而且SQLite库也针对移动设备上的使用进行了优化。

SQLite的接口使用C语言写的,而且 Objective-C是C的超集,所以可以直接在工程中使用SQLite

SQLite占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了,而且处理速度比Mysql、PostgreSQL这两款著名的数据库都还快

在Xcode引入SQLite API

  1. 项目配置界面,选择Build Phrases
    在这里插入图片描述
  2. 点开Link Binary With Libraries后,点击+号,子弹窗输入sqlite
    请添加图片描述
  3. 添加任意一个,两个都是一样的
    请添加图片描述
  4. 在需要使用SQLite的地方导入sqlite库即可
#import <sqlite3.h>

@interface ViewController () {
    sqlite3* _sqlite;
}
@end

SQL语句的种类

数据定义语句(DDL:Data Definition Language)

  • 定义和管理SQL数据库中所有对象(一般是表TABLE,主要用在定义和改变的结构、数据结构、之间的链接和约束等初始化工作上
  • 包括在数据库中创建新表、删除表或改变表(CREATE TABLEDROP TABLEALTER TABLE)等操作

数据操作语句(DML:Data Manipulation Language)

  • 对表(TABLE)中的数据进行操作
  • 包括INSERTDELETEUPDATESELECT等操作

数据查询语句(DQL:Data Query Language)

  • 查询获得表中的数据
  • 基本结构是由SELECT子句、FROM子句、WHERE子句组成的查询块
  • 还包括order bygroup byhaving

数据控制语句(DCL:Data Control Language)

  • 定义数据库、表、字段、用户的访问权限和安全级别,并控制不同数据的直接许可和访问级别,对数据库进行实时监控
  • 包括COMMIT提交、ROLLBACK回滚、GRANTREVOKE

存储字段类型

存储类描述
NULL空值
INTEGER带符号的整型值
REAL浮点值
TEXT文本字符串,使用数据库编码(UTF-8、UTF-16BE或UTF-16LE)存储
BLOB二进制数据(比如文件、模型),完全根据它的输入存储

实际上SQLite字段是无类型的,就算声明为INTEGER类型,仍能存储字符串文本(主键除外),建表时声明任何类型或不声明类型都可以(比如CREATE TABLE TB_One(name, age)

为了保持良好的编程规范,方便程序员之间的交流,编写建表语句的时候最好加上每个字段的具体类型,也是约定俗成的规则

SQLite的使用

创建数据库

实际上是打开指定数据库,有数据库文件就去打开它,没有就自动创建数据库

1. 获取数据库文件路径

    //SQLite文件名称
    static NSString* const dataBaseName = @"MyFirstDB.sqlite";
    
    //将数据库存到沙盒中的Documents路径
    //获取Documents路径
    NSString* sandBoxPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    
    //拼接得到数据库文件地址
    NSString* filePath = [sandBoxPath stringByAppendingPathComponent: dataBaseName];

SQLite文件名称自己随便起,后缀最好是.db.sqilte,用来表示这是一个数据库

2. 打开指定数据库

NSInteger status = sqlite3_open(filePath.UTF8String, &_sqlite);
if (status == SQLITE_OK) {
    NSLog(@"打开数据库成功");
} else {
    NSLog(@"打开数据库失败");
}
  • sqlite3_open(const char *filename, sqlite3 **ppDb);方法用于打开数据库连接
  • 第一个参数是数据库文件的路径,第二个参数是一个数据库的实例,即一个sqlite3*类型的引用(声明),如果要进行增删改查,就得操作_sqlite这个实例。
  • 这个方法有一个枚举返回值表示是否打开了数据库,这里先用整型变量status接收这个返回值
  • filePath是OC类型的字符串,但sqlite3_open要求传递的是C类型的字符串,使用UTF8String将OC风格的字符串转成C风格的字符串

枚举值 SQLITE_OK 代表成功的状态:

在这里插入图片描述
查看沙盒内的数据库文件:

请添加图片描述

创建表和删表

当我们第一次打开数据库后,数据库里面什么都没有,我们需要创建一张数据库表来存放数据

1. 创建表基本格式

  • CREATE TABLE 表名 (字段名1 字段类型 约束, 字段名2 字段类型2 约束, …);
  • CREATE TABLE IF NOT EXISTS表明 (字段名1 字段类型 约束, 字段名2 字段类型2 约束, …); 判断表是否已存在,不存在则创建,防止创建重复的表

示例:CREATE TABLE t_Student(ID INTEGER, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL);NAME和AGE后面的NOT NULL约束表示在表中创建记录时这个字段不能为NULL,常见的约束还有DEFAULTUNIQUEPRIMARY KEY约束,更多SQLite约束的总结详见这篇文章:SQLite约束总结

NSInteger state = sqlite3_open(filePath.UTF8String, &_sqlite);
    if (state == SQLITE_OK) {
        NSLog(@"打开数据库成功");
        
        //创建表SQL语句
        NSString* SQLString = @"CREATE TABLE IF NOT EXISTS t_Student(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL)";
        
        //保存错误信息
        char* errorMsg = NULL;
        NSInteger result = sqlite3_exec(_sqlite, SQLString.UTF8String, NULL, NULL, &errorMsg);
        if (result == SQLITE_OK) {
            NSLog(@"创表成功");
        } else {
            NSLog(@"创表失败 -> errorMessage: %s", errorMsg);
        }
        
    } else {
        NSLog(@"打开数据库失败");
    }
  • sqlite3_exec(sqlite3 *, const char *sql, int (*callback)(void *, int, char **, char **), void *, char **errmsg);方法用于执行除了查询语句以外的其他语句
  • 此方法需要传递5个参数,第一个参数是数据库的引用即sqlite3*类型的引用_sqlite,第二个参数是要执行的SQL语句,第三个参数为回调参数,是一个指向函数的指针,此函数将在SQL语句执行完成后执行,如果把callback前的*改成^就是一个代码块,第四个参数为传递给回调函数的指针参数(可写为NULL),第五个参数为报错信息,用以代码调试
  • 这个方法有一个与sqlite3_open一致的枚举类型返回值,因为我们可以访问到报错信息errorMsg,所以可以不用变量result保存来获取创建表的成功与否,如下:
//直接用C语言风格字符串也可
const char* SQLString = "CREATE TABLE IF NOT EXISTS t_Student(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL)";
        
char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString, NULL, NULL, &errorMsg);
if (!errorMsg) {
    NSLog(@"创表成功");
} else {
    NSLog(@"创表失败 -> errorMessage: %s 当前文件路径:%s 当前行数:%d", errorMsg, __FILE__, __LINE__);
}

__FILE____LINE__为标准C语言中的预定义宏,前者用以指示本行语句所在源文件的文件,后者用以指示本行语句所在源文件中的位置信息

执行后,创建表成功,打开数据库文件查看:
先在终端输入sqlite3 数据库文件路径,输入.tables查看有哪些数据库表,.quit退出数据库文件在这里插入图片描述可以看到不仅有里面创建的t_Student表Table,还有我之后创建的t_Fellow


电脑一般不会预装相关查看sqlite数据库的软件,这里通过Mac终端查看Sqlite,参考Mac通过终端使用Sqlite3数据库


既然已经成功创建了表,现将SQL语句中的IF NOT EXISTS删去,执行创表失败相关语句:在这里插入图片描述错误信息也会提示该表已经创建!

2. 有关主键

良好的数据库编程规范应该要保证每条记录的唯一性,为此增加了主键约束,每张表必须有一个主键,用来表示记录的唯一性,创表时用PRIMARY KEY声明一个主键

示例:CREATE TABLE t_Student(ID INTEGER PRIMARY KEY, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL);
只要声明为PRIMARY KEY,就说明是一个主键字段,主键字段默认包含了NOT NULLUNIQUE两个约束

主键的设计原则:

  • 主键应当是对用户没有意义的
  • 永远也不要更新主键
  • 主键不应包含动态变化的数据
  • 主键应当由计算机自动生成

如果想要让主键自动增长(必须是INTEGER类型),应该增加AUTOINCREMENT,示例:CREATE TABLE t_Student (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER NOT NULL, SCORE REAL);

3. 删除表基本格式

  • DROP TABLE 表名;
  • DROP TABLE IF EXISTS 表名; 判断表是否存在,存在则删除

仍使用sqlite3_exec执行:

const char* SQLString = "DROP TABLE t_Fellow";
        
char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString, NULL, NULL, &errorMsg);
if (!errorMsg) {
    NSLog(@"删表成功");
} else {
    NSLog(@"删表失败 -> errorMessage: %s 当前文件路径:%s 当前行数:%d", errorMsg, __FILE__, __LINE__);
}

t_Fellow表已删:
请添加图片描述

如果删除不存在的表,错误信息为:
请添加图片描述

数据表操作

增(插入数据INSERT)
  • INSERT INTO 表名 (字段1, 字段2, …) values (字段1的值, 字段2的值, …);

示例:INSERT INTO t_Student (NAME, AGE) VALUES ('Jacky', 20);数据库中的字符串内容应该用单引号``括起来

for (int i = 0; i < 7; ++i) {
    NSString* name = [NSString stringWithFormat: @"Jacky-%d号", i + 1];
    NSString* SQLString = [NSString stringWithFormat: @"INSERT INTO t_Student (NAME, AGE) VALUES ('%@', %d)", name, i + 19];

    char* errorMsg = NULL;
    sqlite3_exec(_sqlite, SQLString.UTF8String, NULL, NULL, &errorMsg);
    if (!errorMsg) {
        NSLog(@"插入数据成功");
    } else {
        NSLog(@"插入数据失败 -> errorMessage: %s", errorMsg);
    }
}

查看打印日志:

请添加图片描述

终端查看数据库表的内容:

请添加图片描述

删(删除数据DELETE)
  • DELETE FROM 表名; 删除表中所有数据
  • DELETE FROM 表名 WHERE 查询条件; 删除指定数据
const char* SQLString = "DELETE FROM t_Student WHERE NAME = 'Jacky-4号'";
        
char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString, NULL, NULL, &errorMsg);
if (!errorMsg) {
    NSLog(@"删除数据成功");
} else {
    NSLog(@"删表数据失败 -> errorMessage: %s", errorMsg);
}

请添加图片描述

改(更新数据UPDATE)
  • UPDATE 表名 SET 字段1 = 更新值, 字段2 = 更新值, … ; 改变所有字段为字段1字段2的值
  • UPDATE 表名 SET 字段1 = 更新值, 字段2 = 更新值, … WHERE 查询条件; 改变指定字段数据
const char* SQLString = "UPDATE t_Student SET NAME = 'Jackson-2号' WHERE AGE = 20";

char* errorMsg = NULL;
sqlite3_exec(_sqlite, SQLString, NULL, NULL, &errorMsg);
if (!errorMsg) {
    NSLog(@"更新数据成功");
} else {
    NSLog(@"更新数据失败 -> errorMessage: %s", errorMsg);
}

请添加图片描述

查(查询数据SELECT)
  • SELECT * FROM 表名; 获取所有可用字段
  • SELECT 字段1, 字段2, … FROM 表名; 查询指定字段数据

SELECT查询操作也可以使用sqlite3_exec执行,但通常使用sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail);函数,该函数将SQL语句编译成SQLite虚拟机指令,并将编译后的SQLite3语句对象存储在ppStmt指向的指针中,以备执行。如果pzTail不是NULL,则该指针将指向SQL语句中未编译部分的起始位置

  • sqlite3_prepare_v2函数是用来做SQL查询之前的准备工作的,其返回一个枚举值作为准备工作的结果
  • 该函数第一个参数是sqlite3*类型数据库的引用,第二个参数是要执行的SQL语句,第三个参数是SQL语句的长度(如果设置为-1,则代表系统会自动计算SQL语句的长度),第四个参数用于存储查询结果,第五个参数是指向无法使用的部分的指针,一般不会用到,写成NULL即可
//查询age不等于19的数据
const char* SQLString = "SELECT ID, NAME, AGE FROM t_Student WHERE AGE != 19";
        
//用stmt取出查询结果
sqlite3_stmt* stmt = NULL;
        
NSInteger state = sqlite3_prepare_v2(_sqlite, SQLString, -1, &stmt, NULL);
if (state == SQLITE_OK) {
    NSLog(@"查询语句没有问题 读取数据成功");
            
    //每调用一次sqlite3_step函数,stmt就会指向下一条数据
while (sqlite3_step(stmt) == SQLITE_ROW) {  //找到一条记录
        //读取数据
                
        //取出第0列字段的值(int类型)
        int ID = sqlite3_column_int(stmt, 0);
        //取出第1列字段的值(text类型)
        const unsigned char* name = sqlite3_column_text(stmt, 1);
        //取出第2列字段的值(int类型)
        int age = sqlite3_column_int(stmt, 2);
                
        printf("%d %s %d\n", ID, name, age);
    }
} else {
    NSLog(@"查询语句有问题 读取数据失败");
}
  • sqlite3_step(stmt)函数将会执行查询并且将查询到的当前记录存入到stmt
  • 此函数第一次执行会将表中的第一条数据存到stmt中,第二次执行会将表中的第二条数据存到stmt
  • 因此while (sqlite3_step(stmt) == SQLITE_ROW)将会遍历表中的数据,而SQLITE_ROW枚举值判断的是有读取到数据的行的情况

执行情况:

请添加图片描述

补充

条件语句

如果只想给内容更新或者删除某些指定的数据,就需在DML语句上加上条件语句

以下为条件语句常见格式:

  • WHERE 字段 = 某个值; 注意只有一个=
  • WHERE 字段 is 某个值; is相当于=
  • WHERE 字段 != 某个值;
  • *WHERE 字段 is not 某个值; *is not相当于!=
  • WHERE 字段 > 某个值;
  • WHERE 字段1 = 某个值 and 字段2 > 某个值; and相当于C语言中的&&
  • WHERE 字段1 = 某个值 or 字段2 = 某个值; or相当于C语言中的||

示例:将t_Student表中年龄小于等于25或者年龄大于21的数据,NAME都改为JackyUPDATE t_Student SET NAME = 'Jackson' WHERE AGE > 21 and AGE <= 25;

请添加图片描述
为方便演示,我们再改回去

起别名

字段名、表名起别名,基本格式如下:

  • SELECT 字段1 别名, 字段2 别名, … FROM 表名 别名;
  • SELECT 字段1 别名, 字段2 as 别名, … FROM 表名 as 别名;
  • SELECT 别名.字段1, 别名.字段2, … FROM 表名;

示例:
给字段起别名SELECT NAME USER, AGE AS OLD, SCORE POINT FROM t_Student;
请添加图片描述

给表t_Student起个别名s, 利用s来引用表中的字段SELECT s.NAME, s.AGE FROM t_Student s;
请添加图片描述

计算数据的数量

基本格式:

  • SELECT COUNT (字段) FROM 表名;
  • SELECT COUNT ( * ) FROM 表名;

示例:SELECT COUNT (*) FROM t_Student WHERE AGE > 21;

请添加图片描述

排序

根据某个字段的值进行排序搜索,基本格式如下:

  • SELECT * FROM 表名 ORDER BY 字段; 示例:SELECT * FROM t_Student ORDER BY AGE
  • 默认是按照升序排序(从小到大),也可以写为SELECT * FROM t_Student ORDER BY AGE ASC;也可以变为降序(从大到小)SELECT * FROM t_Student ORDER BY AGE DESC
    在这里插入图片描述
  • 用多个字段进行排序,先根据年龄排序(升序),年龄相等就根据ID排序(降序)SELECT * FROM t_Student ORDER BY AGE ASC, ID DESC;
    请添加图片描述
LIMIT

使用LIMIT可以精确地控制查询结果的数量,基本格式如下:

  • SELECT * FROM 表名 LIMIT 数值1, 数值2;

示例:比如现在有6条数据,SELECT * FROM t_Student LIMIT 2, 4;就表示跳过最前面2条数据,然后取4条数据

请添加图片描述

LIMIT经常用来做分页查询,比如每页固定显示7条数据,那么应该这样取数据:

第1页:LIMIT 0, 7 第2页:LIMIT 7, 7 第3页:LIMIT 14, 7 … 第n页:LIMIT (n - 1)*7, 7


结束语

本文总结了在iOS开发中用OC语言如何去使用SQLite数据库,介绍了SQLite相关函数,以及SQLite的基本语句

iOS中原生的SQLite API在进行数据存储的时候,需要使用C语言中的函数,操作比较麻烦,于是就出现了一系列将SQLite封装的库:CoreData(Apple官方)、FMDB(第三方),后续将会对这两个基于SQLite编写的库进行学习!🫡

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习还可以从如下方面进行创新!!

文章目录 一、我认为可以从如下5个方向进行创新总结 一、我认为可以从如下5个方向进行创新 新的模型结构&#xff1a;尽管现在的深度学习模型已经非常强大&#xff0c;但是还有很多未被探索的模型结构。探索新的模型结构可以带来更好的性能和更低的计算成本。 新的优化算法&a…

[渗透测试学习] Devvortex - HackTheBox

文章目录 信息搜集解题步骤提交flag 信息搜集 扫描端口 nmap -sV -sC -p- -v --min-rate 1000 10.10.11.242发现80端口有http服务&#xff0c;并且是nginx服务 尝试访问web界面&#xff0c;发现跳转到http://devvortex.htb/无法访问 我们用vim添加该域名即可 sudo vim /etc/…

fatal error: sql.h: No such file or directory的解决办法

Ubuntu环境下运行命令 sudo apt install unixodbc-dev 来源&#xff1a;https://github.com/mkleehammer/pyodbc/issues/441

HHDESK右键管理简介

在HHDESK管理文件&#xff0c;除了基本的打开、删除、复制、粘贴、重命名外&#xff0c;还有多种便捷编辑方式。 可以分别以下列模式打开文档&#xff1a; 文本模式即是以文本编辑器打开文档。 1 二进制模式 可进行二进制编辑。 2 JSON模式 可对JSON文件进行直观的解析…

图空图床图片外链系统源码-支持自定义权限策略-图片大小格式

含视频搭建教程。 大致功能&#xff1a; 支持本地等多种第三方云储存 AWS S3、阿里云 OSS、腾讯云 COS、七牛云、又拍云、SFTP、FTP、WebDav、Minio多种数据库驱动支持&#xff0c;MySQL 5.7、PostgreSQL 9.6、SQLite 3.8.8、SQL Server 2017支持配置使用多种缓存驱动&#xff…

免费的数据采集软件,最新免费的几款数据采集软件【2024】

在当今数字化时代&#xff0c;数据是企业决策和业务发展的关键。而如何高效获取数据成为许多企业和研究机构的关注焦点。本文将深入探讨数据采集软件的种类。帮助大家选择最适合自己需求的数据采集工具。 数据采集软件种类 在众多数据采集软件中&#xff0c;有一类强大而多样…

微信小程序 - PC端选择ZIP文件

微信小程序 - PC端选择文件 分享代码片段场景分析解决思路附魔脚本chooseMediaZip 选择附魔后的ZIP文件相关方法测试方法 参考资料 分享代码片段 不想听废话的&#xff0c;直接看代码。 https://developers.weixin.qq.com/s/UL9aojmn7iNU 场景分析 如果你的微信小程序需要选…

【深度学习】强化学习(二)马尔可夫决策过程

文章目录 一、强化学习问题1、交互的对象2、强化学习的基本要素3、策略&#xff08;Policy&#xff09;4、马尔可夫决策过程1. 基本元素2. 交互过程的表示3. 马尔可夫过程&#xff08;Markov Process&#xff09;4. 马尔可夫决策过程&#xff08;MDP&#xff09;5. 轨迹的概率计…

蓝牙物联网对接技术难点有哪些?

#物联网# 蓝牙物联网对接技术难点主要包括以下几个方面&#xff1a; 1、设备兼容性&#xff1a;蓝牙技术有多种版本和规格&#xff0c;如蓝牙4.0、蓝牙5.0等&#xff0c;不同版本之间的兼容性可能存在问题。同时&#xff0c;不同厂商生产的蓝牙设备也可能存在兼容性问题。 2、…

联邦多任务蒸馏助力多接入边缘计算下的个性化服务 | TPDS 2023

联邦多任务蒸馏助力多接入边缘计算下的个性化服务 | TPDS 2023 随着移动智能设备的普及和人工智能技术的发展,越来越多的分布式数据在终端被产生与收集&#xff0c;并以多接入边缘计算(MEC)的形式进行处理和分析。但是由于用户的行为模式与服务需求的多样,不同设备上的数据分布…

Navicat 技术指引 | 连接 GaussDB 分布式

Navicat Premium&#xff08;16.3.3 Windows 版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB 分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结…

Django的logging-日志模块的简单使用方法

扩展阅读&#xff1a; Python-Django的“日志功能-日志模块(logging模块)-日志输出”的功能详解 现在有下面的Python代码&#xff1a; # -*- coding: utf-8 -*-def log_out_test(content_out):print(content_out)content1 "i love you01" log_out_test(content1)现…

用C++实现队列顺序结构的基本操作

//队列顺序结构的基本操作&#xff1a; #include"stdio.h" #include"String" #define QueueSize 100 typedef char ElemType; typedef struct { ElemType data[QueueSize]; /*保存队中元素*/int front,rear; /*队头和队尾指针*/ } SqQueue; void Init…

android项目实战之编辑器集成

引言 项目需要用到编辑器&#xff0c;采用RichEditor&#xff0c;如下效果 实现 1. 引入库2 implementation jp.wasabeef:richeditor-android:2.0.0 2. XML <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width&q…

【计算机网络】应用层电子邮件协议

一、电子邮件系统架构 电子邮件是一个典型的异步通信系统&#xff0c;发送方从UA&#xff0c;也就是邮件客户端&#xff0c;通过应用层SMTP协议&#xff0c;传输层tcp协议&#xff0c;发送给发送方的邮件服务器&#xff0c;比如使用的是163邮箱&#xff0c;163提供的SMTP服务器…

C++ day59 下一个更大元素Ⅱ 接雨水

题目1&#xff1a;503 下一个更大元素Ⅰ 题目链接&#xff1a;下一个更大元素Ⅱ 对题目的理解 返回循环数组中每个元素的下一个更大元素&#xff0c; 数字x的下一个更大元素是循环等的搜索它的最近的下一个更大的数 数组的中至少有一个元素 本题难点在于循环遍历这里&…

基于深度学习yolov5实现安全帽人体识别工地安全识别系统-反光衣识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 实现安全帽人体识别工地安全识别系统需要使用深度学习技术&#xff0c;特别是YOLOv5算法。下面是对基于YOLOv5实现安…

[mac系统]利用换行符查找替换^p 报错 --caption_column‘ calue ‘test‘ needs to be one of: image

报错内容 代码内容 args.image_column "image" args.caption_column "text" 问题原因&#xff1a; 训练过程需要blip文件是metadata.json格式 ​ 测试过程需要的文件是txt格式 blip.txt​ ​ 解决办法 1 利用word查找替换 用{"file_name": &…

银行卡二要素API的应用案例:从在线购物到金融投资

引言 随着互联网技术的不断发展&#xff0c;人们的金融需求也在不断增加。随之而来的是各种新型金融服务的涌现&#xff0c;让用户的金融体验更加便利快捷。其中&#xff0c;银行卡二要素API的应用&#xff0c;则为用户的金融体验和安全性提供了极大的保障。 银行卡二要素API…

【C++数据结构 | 图速通】10分钟掌握邻接矩阵 邻接表 | 快速掌握图论基础 | 快速上手抽象数据类型图

图 by.Qin3Yu 请注意&#xff1a;严格来说&#xff0c;图不是一种数据结构&#xff0c;而是一种抽象数据类型。但为了保证知识点之间的相关性&#xff0c;也将其列入数据结构专栏。 本文需要读者掌握顺序表和单链表的操作基础&#xff0c;若需学习&#xff0c;可参阅我的往期文…