Fork me on GitHub

mybatis配置解析

mybatis配置解析

入门_MyBatis中文网

在介绍mybatis配置解析前 首先我们复习一下

创建一个使用mybatis框架的子项目的方法:

  1. 在父项目下新建一个Module(普通的maven项目)
  2. 在main、resource下写核心配置文件(mybatis-config.xml)
  3. 写工具类(untils)
  4. 写实体类(pojo)
  5. 写接口类(Mapper)

配置解析

核心配置文件(mybatis-config.xml)

这个核心配置文件的基本结构为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>

环境变量(environments)

基本结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

environments这里是写的复数,因此其实可以写很多套environment,但能使用一套 即default参数指向的那套

属性(properties)

如在environment里就有property

properties里有三种引入方法

第一种 不引入 直接在enviroment里写

1
2
3
4
5
6
7
8
9
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEnconding=utf8&amp;useSSL=true"/>
<property name="username" value="dwx"/>
<property name="password" value="123456"/>
</dataSource>
</environment>

第二种 在xml中的<properties></properties>键值对里写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<properties>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEnconding=utf8&amp;useSSL=true"/>
<property name="username" value="dwx"/>
<property name="password" value="123456"/>
</properties>

<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

当然 此时 我们可以看到 在使用配置参数时 就直接使用${}引入

第三种 在db.properties中编写 在<properties></properties>中引入

db.properties:

1
2
3
4
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEnconding=utf8&useSSL=true
username = dwx
password = 123456

mybaties-config.xml:

1
2
3
4
5
6
7
8
9
10
11
12
<properties resource="db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

他们的基本结构是:

两个文件的结构

我们注意 当同时存在外部引入和内部参数时,外部引入的优先级更高

mybatis进阶版的增删改查

mybatis的进阶版的增删改查是包括两个方面:“用map来代替参数”,“模糊查询”

用map来代替参数

原因:map里边存的是键值对,<”key”,value>,要使用时就直接通过调用键值对来实现

map存储的数据没有大小,没有属性限制,因此使用起来更加方便

UserMapper的方法:

1
2
3
4
student getStudentMap(Map<String,Object> map);
//对于需要对数据库进行操作的操作 以添加用户为例
void InsertStudentMap(Map<String,Object> map);
//测试模糊查询

xml里进行配置

1
2
3
4
5
6
7
8
<!--使用map的查询-->
<!--map参数类型直接填map-->
<select id="getStudentMap" parameterType="map" resultType="com.dwx.pojo.student">
SELECT * FROM mybatis.student WHERE id = #{studentid}
</select>
<insert id="InsertStudentMap" parameterType="map">
insert into mybatis.student (id,name,password) VALUES (#{StudentID},#{StudentName},#{StudentPassword})
</insert>

测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
public void getStudentMap(){
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
//因为是使用map作为参数类型 因此需要创建map
Map<String, Object> map = new HashMap<>();
map.put("studentid",1);
student studentMap = mapper.getStudentMap(map);
System.out.println(studentMap.getId());
//注意查询的要点在于设置返回值类型
sqlSession.close();
}
@Test
public void InsertStudnetMap(){
//固定格式得到mapper
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
//设置Map
Map<String, Object> map = new HashMap<>();
map.put("StudentID",100);
map.put("StudentName","李一百");
map.put("StudentPassword","welcome");
mapper.InsertStudentMap(map);
//一定要记得 对于数据库数据有改动的操作一定要提交事务
sqlSession.commit();
//用完关闭资源
sqlSession.close();

}

模糊查询

注意:模糊查询在sql语句中的特殊点就是 把“=”代替为“like” 把参数需要进行模糊的地方使用%代替如%参数%

问题来了,这个%%的插入可以是在SQL里直接带的 也可以是传参时传递过去的,因此这里产生了两种模糊查询的方法

直接在sql语句中添加%%

xml中的配置

1
2
3
<select id="getList1" parameterType="String" resultType="com.dwx.pojo.student">
SELECT * FROM mybatis.student WHERE name like "%"#{name}"%"
</select>

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
public void getList(){
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
//模糊查询的关键在于模糊的地方用%代替
//因此出现了两种模糊查询的方法
//第一种是在传参是直接将%%带上传过去
//第二种方式是在SQL中固定添加%% 参数只需要传想要查询的模糊参数
List<student> list = mapper.getList1("李");
for (student student : list) {
System.out.println(student.getId()+" "+student.getName()+" "+student.getPassword());
}
}

传参时带上%%

xml中的配置:

1
2
3
<select id="getList1" parameterType="String" resultType="com.dwx.pojo.student">
SELECT * FROM mybatis.student WHERE name like #{name}
</select>

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void getList(){
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
//模糊查询的关键在于模糊的地方用%代替
//因此出现了两种模糊查询的方法
//第一种是在传参是直接将%%带上传过去
List<student> list = mapper.getList1("%李%");
for (student student : list) {
System.out.println(student.getId()+" "+student.getName()+" "+student.getPassword());
}


}

mybatis增删改查操作基础


由于mybatis就是对永久层简化的一种操作,器主要的操作就是对数据库的增删改查,因此今天我将介绍mybatis增删改查操作的基础

增删改查操作实现的步骤

与Jdbc相同,mybatis的增删改查工作也是有很多的共性的,区别只存在于sql语句的编写

共同步奏:

  1. 编写mapper接口类 创建方法
  2. 编写mapper接口类的实现xml文件
  3. 使用

SELECT 查询

查询可以分为两种:1.查询全部内容,2.通过参数参训

查询全部内容(无参查询)

1
2
3
4
5
6
<!--与Dao类的实现类一样 这里是写sql语句-->
<!--id指的是我们调用这个sql使用的名字,可以理解为BaseDaoImp的方法名-->
<!--resultType表示返回的数据的类型 如果是自己创建的类 需要填写整个路径-->
<select id="getList" resultType="com.dwx.pojo.student">
select * from mybatis.student
</select>

元素分析:

  • id与mapper抽象类中的方法相同
  • resulttype指的是返回值类型 如果是基本数据类型直接写基本数据类型,如果是自定义数据类型就将全部的路径都写下来

查询部分内容(有参查询)

1
2
3
4
5
<!--parameterType表示参数类型-->
<!--通过特定的参数查询,一定要写好参数类型 一般查收农户类型直接写 自定义的参数类型就写参数的全路径-->
<select id="getStudentById" resultType="com.dwx.pojo.student" parameterType="int">
select * from mybatis.student where id = #{id}
</select>

元素分析:

  • id与mapper抽象类中的方法相同
  • resulttype指的是返回值类型 如果是基本数据类型直接写基本数据类型,如果是自定义数据类型就将全部的路径都写下来
  • parameterType表示参数类型 在这个xml文档是 通过#{参数名} 来获得参数的 获得的参数来自id指的方法

INSERT 插入

插入即增加

1
2
3
4
<!--我们可以直接把参数类型中的子类型提取出来使用-->
<insert id="InsertStudent" parameterType="com.dwx.pojo.student">
insert into mybatis.student (id,name,password) values(#{id},#{name},#{password})
</insert>

注意:插入操作的实现是需要提交事务的,写到实现方法时会再次强调

插入没有返回值类型

  • id与mapper抽象类中的方法相同
  • parameterType表示参数类型 在这个xml文档是 通过#{参数名} 来获得参数的 获得的参数来自id指的方法

DELETE 删除

1
2
3
4
<!--删除操作-->
<delete id="DeleteStudent" parameterType="int">
delete from mybatis.student where id = #{id}
</delete>

删除没有返回值类型

  • id与mapper抽象类中的方法相同
  • parameterType表示参数类型 在这个xml文档是 通过#{参数名} 来获得参数的 获得的参数来自id指的方法

UPDATE SET 修改

更新 设置 即为修改

1
2
3
4
<!--修改用户信息-->
<update id="UpdateStudent" parameterType="com.dwx.pojo.student">
update mybatis.student set name = #{name},password = #{password} where id = #{id}
</update>

修改没有返回值类型

  • id与mapper抽象类中的方法相同
  • parameterType表示参数类型 在这个xml文档是 通过#{参数名} 来获得参数的 获得的参数来自id指的方法

mapper/dao 抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.dwx.dao;

import com.dwx.pojo.student;

import java.util.List;

//编写一个抽象的DAO接口
public interface UserDao {
//编写这个接口需要的方法
//查询所有的用户
List<student> getList();
//通过id查询用户
student getStudentById(int id);
//增加用户
void InsertStudent(student student);
//修改用户信息
void UpdateStudent(student student);
//通过id删除用户信息
void DeleteStudent(int id);
}

实现类(测试类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.dwx.dao;

import com.dwx.pojo.student;
import com.dwx.untils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaoTest {
@Test
public void test(){
//调用我们的myBatisUtils来获得是SqlSession
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
//sqlSession进行操作
//通过反释来获得UserMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
//获得的mapper对象就能调用“id”,来实现操作了
List<student> list = mapper.getList();
for (student student : list) {
System.out.print(student.getId());
System.out.print(student.getName());
System.out.println(student.getPassword());
}
sqlSession.close();

}
//通过参数查询的测试方法
@Test
public void getStudentById(){
//这些都是固定格式
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
//sqlSession获得联结
UserDao mapper = sqlSession.getMapper(UserDao.class);
student studentById = mapper.getStudentById(2);
System.out.println(studentById);
System.out.println(studentById.getId()+studentById.getName()+studentById.getPassword());


sqlSession.close();
}
@Test
public void InsertStudnet(){
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
//获得了sqlSession就需要进行联结获得mapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
//通过mapper对象来进行操作
mapper.InsertStudent(new student(4,"王五","123456"));
//对于需要对数据库进行改动的如:“增删改” 都系要提交事物
sqlSession.commit();
//关闭sqlsession
sqlSession.close();
}
@Test
public void update(){
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();

UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.UpdateStudent(new student(3,"李四","123456"));
//注意必须提交事务
sqlSession.commit();
sqlSession.close();
}
@Test
public void delete(){
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.DeleteStudent(3);
//提交事务
sqlSession.commit();
sqlSession.close();
}
}

注意,JDBC中有链接(getconnection)就完成了事务的提交,但是mybatis需要手动提交事务。

就是在执行完对数据库有改动的操作(增删改)后需要加一个语句sqlSession.commit()

第一个mybatis程序

对于mybatis的学习应该尽量参考他的中文官方文档

文档地址: MyBatis中文网

mybatis是对永久层进行简化的几个框架,也就是说,它能够方便对数据库的操作

因此需要先创建一个数据库

用sql语句创建数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE DATABASE `mybatis`;

USE `mybatis`;


CR`mybatis``student`EATE TABLE `student`(
`id` INT(20) NOT NULL PRIMARY KEY,
`name` VARCHAR(20) DEFAULT NULL,
`password` VARCHAR(20) DEFAULT NULL
)ENGINE = INNODB DEFAULT CHARSET = utf8;

`student`INSERT INTO `student`(`id`,`name`,`password`)VALUES
(1,'张三','welcome'),
(2,'李四','welcome'),
(3,'王五','welcome')

创建数据库的样式

mybatis框架

mybatis是一个框架,是对数据库进行操作的,也需要创建测试类

因此需要导入的依赖有:mybatis、mysql、junit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!--对于mybatis需要 mysql mybatis Junit 等的依赖-->
<dependencies>
<!--mybatis依赖-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.9</version>
</dependency>
<!--mysql依赖-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!--junit依赖-->
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>

编写第一个mybatis程序

准备工作

  1. 创建一个空的maven项目
  2. 删除src文件夹
  3. 导包(这样就获得了父项目)
  4. 创建module,maven(这样就获得了子程序)

创建pojo层

以及与数据库中表格对应的映射类 以及get、set方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.dwx.pojo;

//创建与数据库映射的类
public class student {
private int id;
private String name;
private String password;

//无参构造
public student() {
}

//有参构造
public student(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}

//get和set方法
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}

编写Dao层

使用mybatis编写的Dao层,也可以称为Mapper层包括一个抽象的类 以及实现他的XML

抽象的UserDao

1
2
3
4
5
6
7
8
9
10
11
12
package com.dwx.dao;

import com.dwx.pojo.student;

import java.util.List;

//编写一个抽象的DAO接口
public interface UserDao {
//编写这个接口需要的方法
List<student> getList();
}

UserMapper.xml实现抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8" ?>
<!--这里是固定的格式,前边的都是文件配置,不需要更改-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--这里是我们需要更改的地方 namespace表示 我们的Dao层在哪里(抽象类)-->
<mapper namespace="com.dwx.dao.UserDao">
<!--与Dao类的实现类一样 这里是写sql语句-->
<!--id指的是我们调用这个sql使用的名字,可以理解为BaseDaoImp的方法名-->
<!--resultType表示返回的数据的类型 如果是自己创建的类 需要填写整个路径-->
<select id="getList" resultType="com.dwx.pojo.student">
select * from student
</select>
</mapper>

这个xml里上边的是文件参数,不需要更改

需要更改的我都加有注释,可以看注释理解

编写untils层

untils层包括使用MyBatis进行操作的类以及方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.dwx.untils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

//固定的格式
public class MyBatisUtils {
//直接读取resources文件夹下的配置文件
String resource = "mybatis-config.xml";
//将这个文件转换成输入流
InputStream inputStream;

{
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
}
//将这个转化好的输入流设计为SqlSession工厂 通过工厂模式来创造SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获得SqlSession的方法
public SqlSession getSqlSession(){
//通过SqlSession工厂直接获得sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}

}

这里使用的是工厂模式,来实现实例化对象.

这个类中的方法都是固定的,不需要进行更改

在这个类中可以看到一个mybatis-config.xml的文件被使用了,这个xml文件中包含着所有的与数据库相连的配置,在Jdbc里,我们使用的是driver、getConnection等等方法进行联结。

编写mybatis-config.xml文件

这个文件必须在main文件夹下的resources文件夹下,这样才容易进行调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--环境配置-->
<!--网上有模版-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEnconding=utf8&amp;useSSL=true"/>
<property name="username" value="dwx"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--我们每一个xml文件都要在这里进行配置-->
<mappers>
<mapper resource="com/dwx/dao/UserMapper.xml"/>
</mappers>
</configuration>

这个配置文件也是固定的格式

不过需要注意这个<mappers></mappers>这个标签对

这个标签对里包括的数据是:在整个程序(子程序)中,凡是创建的使用的xml文件都需要在这里进行配置。否则将会报错

没有配置mapper时报的错

文件分层

编写测试类来进行测试

需要注意的是,这个文件测试类写在与main文件夹同级的test文件夹下的绿色java文件夹中

需要测试哪个,就把哪个对应的全部包等等都创建出来

测试类的编写规范

编写UserDaoTest类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.dwx.dao;

import com.dwx.pojo.student;
import com.dwx.untils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaoTest {
@Test
public void test(){
//调用我们的myBatisUtils来获得是SqlSession
MyBatisUtils myBatisUtils = new MyBatisUtils();
SqlSession sqlSession = myBatisUtils.getSqlSession();
//sqlSession进行操作
//通过反释来获得UserMapper
UserDao mapper = sqlSession.getMapper(UserDao.class);
//获得的mapper对象就能调用“id”,来实现操作了
List<student> list = mapper.getList();
for (student student : list) {
System.out.print(student.getId());
System.out.print(student.getName());
System.out.println(student.getPassword());
}
sqlSession.close();

}
}

必须注意的是,因为我们使用的是xml,但是这个xml并不会出现在target文件夹中,会出错。

这个错误指的是“数据冲突”错误

解决数据冲突错误

方法一:直接把缺少的xml文件夹复制到target中去

方法二:在pom.xml配置文件中添加如下<bulid></build>标签对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>

这个是固定格式 不用修改

在父项目的pom.xml中添加可以作用到每一个子程序

在子项目的pom.xml中添加只能作用到这个子程序

程序运行结果

通过使用mybatis,在大量使用sql语句时方便了SQL语句的编写。

运行结果

C语言学生管理系统(单链表)

学生管理系统(单链表)

某个班的学生,每个学生的信息包括学号、姓名、3门课成绩。从键盘输入学生的数据,要求:能够给用户提供以下功能:要求: 1、找到每门课成绩最高的学生,打印其全部信息;2、查找平均分最高的学生打印其信息;

已经实现了的功能如上所述

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include<stdio.h>
#include<stdlib.h>

typedef struct {
int id;
char name[10];
double math;
double chinese;
double english;
} Student;

typedef struct SNode {
Student elem;
struct SNode* next;
} SNode,*SList;


void Order();
//初始化一个单链表
void init(SList &S);
//输入学生信息
void setSList(SList &S);

//打印整个单链表
void PrintList(SList &S);

//获得平均分最高的
void getMaxNode(SList &S);
//打印单个节点
void PrintNode(SNode* &S);
//获得单个科目的最大值
void getMax(SList &S);
//排序 这里我设计的排序都是使用冒泡排序 冒泡排序是适用于顺序表和链表的
void SortByChinese(SList &S);
void SortByMath(SList &S);
void SortByEnglish(SList &S);
void BubbleSort(SList &S);


int main() {
Order();
SList S;
int order;
scanf("%d",&order);
while(order!=10) {
switch(order) {
case 1:
init(S);
break;
case 2:
PrintList(S);
break;
case 3:
setSList(S);
break;
case 4:
getMaxNode(S);
break;
case 5:
getMax(S);
break;
deflaut:
printf("请输入正确的指令:\n");
break;
}
printf("请输入你要执行的命令:");
scanf("%d",&order);
}
}

void Order() {
printf("1.初始化一个单链表:\n");
printf("2.打印整个单链表\n");
printf("3.输入学生信息\n");
printf("4.打印平均分最大的人\n");
printf("5.分别打印三科分最高的人\n");
printf("请输入你要执行的命令:");
}
void init(SList &S) {
S = (SNode *)malloc(sizeof(SNode));
S->next = NULL;
if(S) {
printf("单链表创建成功!!!\n");
} else {
printf("内存不足,单链表创建失败!!!\n");
}
}
void PrintList(SList &S) {

SList L = (SList)malloc(sizeof(SList));
L = S;
while(L->next) {
printf("%d\t",L->elem.id);
//打印学生姓名
for(int j=1; L->elem.name[j]!='#'; j++) {
printf("%c",L->elem.name[j]);
}
printf("\t");
printf("数学成绩:%lf\t",L->elem.math);
printf("语文成绩:%lf\t",L->elem.chinese);
printf("英语成绩:%lf\t",L->elem.english);
printf("\n");
L = L->next;
}
}
void setSList(SList &S) {
//输入学生信息就是给单链表插入数据的过程
//这里采用的方法是前插法创建链表
int num;
int i=0;
printf("请输入你要输入几个学生信息:");
scanf("%d",&num);
while(num!=0) {
SNode* L = (SNode *)malloc(sizeof(SNode));
printf("请输入学生的学号:");
scanf("%d",&L->elem.id);
getchar();
printf("请输入学生姓名(学生姓名以#结尾):");
i=0;
do {
i++;
scanf("%c",&L->elem.name[i]);
} while(L->elem.name[i]!='#');
printf("请输入学生的数学成绩:");
scanf("%lf",&L->elem.math);
printf("请输入学生的语文成绩:");
scanf("%lf",&L->elem.chinese);
printf("请输入学生的英语成绩:");
scanf("%lf",&L->elem.english);

L->next = S;
S = L;
num--;
}


}
void getMaxNode(SList &S) {
//用一个指针执行平均分最高的
double totalMax;
//给总数一个初始化 因为每人都是三科 平均分的最大值就是总分的最大值
totalMax = 0;
double total;
SNode* LMax = (SNode *)malloc(sizeof(SNode));
SNode* L = (SNode *)malloc(sizeof(SNode));
L = S;
LMax = L;
while(L->next) {
total = L->elem.chinese + L->elem.english + L->elem.math;
if(total > totalMax) {
totalMax = total;
LMax = L;
}
L = L->next;
}
printf("平均分最大的人是:\n");
PrintNode(LMax);

}
void PrintNode(SNode* &S) {

printf("%d\t",S->elem.id);
//打印学生姓名
for(int j=1; S->elem.name[j]!='#'; j++) {
printf("%c",S->elem.name[j]);
}
printf("\t");
printf("数学成绩:%lf\t",S->elem.math);
printf("语文成绩:%lf\t",S->elem.chinese);
printf("英语成绩:%lf\t",S->elem.english);
printf("\n");
}
void getMax(SList &S) {
double englishMax = 0.0;
double chineseMax = 0.0;
double mathMax = 0.0;
double chinese;
double english;
double math;

SNode* LChineseMax = (SNode *)malloc(sizeof(SNode));
SNode* LMathMax = (SNode *)malloc(sizeof(SNode));
SNode* LEnglishMax = (SNode *)malloc(sizeof(SNode));
SNode* L = (SNode *)malloc(sizeof(SNode));
L = S;
while(L->next) {
chinese = L->elem.chinese;
english = L->elem.english;
math = L->elem.math;
if(math > mathMax) {
mathMax = math;
LMathMax = L;
}
if(english > englishMax) {
englishMax = english;
LEnglishMax = L;
}
if(chinese > chineseMax) {
chineseMax = chinese;
LChineseMax = L;
}
L = L->next;
}
printf("数学分最大的人是:\n");
PrintNode(LMathMax);
printf("语文分最大的人是:\n");
PrintNode(LChineseMax);
printf("英语分最大的人是:\n");
PrintNode(LEnglishMax);

}


程序运行截图

过程1

C语言排序法

排序法

这篇博客介绍了多个基本的排序法

也是我常用的 最能理解的排序法

排序的基本概念

**排序:**是按照关键字的非递减或非递增顺序对一组记录重新进行排序的操作

**排序的稳定性:**假设K1=Kj,且在排序前的序列中Ri领先于Rj(即i<j)。若Ri仍领先于Rj。则这个排序法是稳定的。

内部排序和外部排序

内部排序:待排序的记录都在计算机的内存中

外部排序:待排序的数据量很大,将外部数据分块放入内存中,进行排序。

内部排序方法的分类

  • 插入类:插入排序,折半插入排序,希尔排序
  • 交换类:冒泡排序,快速排序
  • 选择类:简单选择排序,树形选择排序,堆排序
  • 归并类:2-路并排序
  • 分配类:唯一一个不需要进行关键字比较的排序方法。

几个排序法的作用总览

这是一个完整的C语言程序 经测是能够运行的

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>


#define MAXSIZE 200000

typedef struct {
int r[MAXSIZE+1];
int length;
} SqList;

//顺序表的初始化
int init(SqList &S);
//插入排序
void InsertSort(SqList &S);
//折半插入排序
void BInsertSort(SqList &S);
//希尔排序 希尔排序要分为两部分进行
void ShellInsert(SqList &S, int dk);
void ShellSort(SqList &S);
//上述的排序都是插入排序
//还有一个大类的排序就是接下来要设计到的 交换排序
//交换排序中的老大哥 冒泡排序
void BubbleSort(SqList &S);
//快速排序 我写的快速排序或许会与书上的不同
void FastSort(SqList &S,int left,int right);
//选择排序 选择排序才是我经常使用的排序法
void SelectSort(SqList &S);


void Print(SqList &S);


void Order();

int main() {
SqList S;
int order;
Order();
scanf("%d",&order);
while(order!=10) {
switch (order) {
case 1:
init(S);
break;
case 2:
Print(S);
break;
case 3:
InsertSort(S);
break;
case 4:
BInsertSort(S);
break;
case 5:
ShellSort(S);
break;
case 6:
BubbleSort(S);
break;
case 7:
FastSort(S,1,S.length);
break;
case 8:
SelectSort(S);
break;
default:
printf("请输入正确的序号!!!\n");
break;
}
printf("请输入你要执行的程序的序号:");
scanf("%d",&order);
}
}


int init(SqList &S) {
srand(time(NULL));
printf("请输入你要创建的顺序表的长度:");
scanf("%d",&S.length);
int i,j;
//初始化顺序表是从1开始的,这样就可以使r[0]作为哨兵
for(i=1; i<=S.length; i++) {
S.r[i] = rand()%1000 + 1;
}
}
void InsertSort(SqList &S) {
//对于插入排序是在排序好的顺序表中插入元素
int i;
int j;
//这里的i=2是因为,对于第一个数据,是已经排好序的了的,只有一个数据的顺序串是一定是有序的
for(i=2; i<=S.length; i++) {
//如果要执行的那个数据小于他之前的数据,就需要对前边进行查找,找到合适的位置,把数据插入
if(S.r[i]<S.r[i-1]) {
//使用哨兵 来存储要实行的数据
S.r[0] = S.r[i];
S.r[i] = S.r[i-1];
//进行移动 如果找到 或 到了哨兵节点 就跳出
for(j=i-2; S.r[0] < S.r[j]; j--) {
S.r[j+1]=S.r[j];
}
S.r[j+1] = S.r[0];
}
}
}

void Print(SqList &S) {
int i;
for(i=1; i<=S.length; i++) {
printf("%d ",S.r[i]);
}
printf("\n");
}

void BInsertSort(SqList &S) {
int i;
int low;
int high;
int mid;
int j;
for(i=2; i<=S.length; i++) {
S.r[0] = S.r[i];
//设计两个区间
//在执行的目标元素之前都是排序好的
low = 1;
high = i-1;
//请注意这里是 <= 不是<
while(low <= high) {
mid = (low+high)/2;
//如果个mid比较大小 目标元素太大了 就提高low 反正降低high
if(S.r[0]<S.r[mid]) {
high = mid-1;
} else {
low = mid+1;
}
}
//如果找到了合适的位置 就要用一般方法进行插入

for(j=i-1; j>=high+1; j--) {
S.r[j+1] = S.r[j];
}
S.r[high+1] = S.r[0];
}
}

void Order() {
printf("请输入你要执行的程序的序号:\n");
printf("1.初始化一个链表\n");
printf("2.展示链表\n");
printf("3.插入排序给链表排序\n");
printf("4.折半排序给链表排序\n");
printf("5.希尔排序给链表排序\n");
printf("6.冒泡排序给链表进行排序\n");
printf("7.快速排序给链表进行排序\n");
printf("8.简单排序给链表进行排序\n");
printf("10.退出程序\n");
}
void ShellSort(SqList &S) {
int k;
int dt[1000];
int i;
printf("请输入要做几次划分:");
scanf("%d",&k);
//对我们的划分进行初始化 最后一个一定要为1 是非递减的输入的
for(i=0; i<k; i++) {
printf("请输入第%d个划分大小",i+1);
scanf("%d",&dt[i]);
}
for(i=0; i<k; i++) {
ShellInsert(S,dt[i]);
}
}
void ShellInsert(SqList &S,int dk) {
//对整个数组机型查分排序
int i,j;
for(i=dk+1; i<=S.length; i++) {
//如果未知序列第一个元素比已经排好序的序列对应的元素小 就调换他们的位置
if(S.r[i]<S.r[i-dk]) {
S.r[0] = S.r[i];

//因为可能一个划分里有多个元素 因此需要进行循环
for(j=i-dk; j>0 && S.r[0]<S.r[j]; j-=dk) {
S.r[j+dk] = S.r[j];
}
//条出循环就说明找到了位置
S.r[j+dk] = S.r[0];
}
}
}


void BubbleSort(SqList &S) {
//冒泡排序就是通过多次的相邻两个元素的比较 交换 实现将最大或最小的元素放到两边
//不同的是 这个冒泡排序标记一个flag 如果在排序中没有出现交换 那么就证明排序已经玩成了 就不需要在继续执行了
int i;
int flag=1;
int m = S.length-1;
while((m>0)&&(flag==1)) {
//在进行循环之前将flag表为0 检测后续是否有数据变化
flag = 0;
for(i=1; i<m; i++) {
//我们执行的是从小到大排序
if(S.r[i+1]<S.r[i]) {
//交换两者位置 并且将flag立为0
S.r[0] = S.r[i+1];
S.r[i+1] = S.r[i];
S.r[i] = S.r[0];
flag = 1;
}
//最大值已经确定 不用再比较
--m;
}
}
}
void FastSort(SqList &S,int left,int right) {
int i,j,t,temp;
if(left>right){
return ;
}
temp = S.r[left];
i = left;
j = right;
while(i!=j){
while(S.r[j]>=temp && i<j){
j--;
}
while(S.r[i]<=temp && i<j){
i++;
}
if(i<j){
t = S.r[i];
S.r[i]=S.r[j];
S.r[j] = t;
}
}
S.r[left] = S.r[i];
S.r[i] = temp;
FastSort(S,left,i-1);
FastSort(S,i+1,right);
return ;
}
void SelectSort(SqList &S){
//每次挑出一个最小值 放到指定的位置 再进行循环 纸质结束
int i;
int j;
int min;
int temp;
for(i=1;i<=S.length;i++){
min = i;
for(j=i;j<=S.length;j++){
if(S.r[j]<S.r[min]){
//找到最小的数
min = j;
}
}
if(min != i){
temp = S.r[i];
S.r[i] = S.r[min];
S.r[min] = temp;
}
}
}

插入排序

基本思想:每一趟将一个待排序的记录,将其关键字的大小插入到已经拍好序的一组记录的适当位置上,直到所有待排序记录全部插入为止。

直接插入排序

直接插入排序:是最简单的插入排序,每一次进行排序都是将数据插入到已经排好序的有序表中,从而得到一个新的、记录数量增1的有序表。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void InsertSort(SqList &S) {
//对于插入排序是在排序好的顺序表中插入元素
int i;
int j;
//这里的i=2是因为,对于第一个数据,是已经排好序的了的,只有一个数据的顺序串是一定是有序的
for(i=2; i<=S.length; i++) {
//如果要执行的那个数据小于他之前的数据,就需要对前边进行查找,找到合适的位置,把数据插入
if(S.r[i]<S.r[i-1]) {
//使用哨兵 来存储要实行的数据
S.r[0] = S.r[i];
S.r[i] = S.r[i-1];
//进行移动 如果找到 或 到了哨兵节点 就跳出
for(j=i-2; S.r[0] < S.r[j]; j--) {
S.r[j+1]=S.r[j];
}
S.r[j+1] = S.r[0];
}
}
}

算法特点:

  1. 稳定排序
  2. 算法简便、且容易实现
  3. 适合于链式存储和顺序存储结构
  4. 更适合于初始记录基本有序的情况,n较大时,算法时间复杂度较高,不宜采用
  5. 时间复杂度为O(n^2)
  6. 空间复杂度为O(1)

折半插入排序

折半插入排序能够节省插入排序的寻找过程,但是不能改变移动过程

算法实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void BInsertSort(SqList &S) {
int i;
int low;
int high;
int mid;
int j;
for(i=2; i<=S.length; i++) {
S.r[0] = S.r[i];
//设计两个区间
//在执行的目标元素之前都是排序好的
low = 1;
high = i-1;
//请注意这里是 <= 不是<
while(low <= high) {
mid = (low+high)/2;
//如果个mid比较大小 目标元素太大了 就提高low 反正降低high
if(S.r[0]<S.r[mid]) {
high = mid-1;
} else {
low = mid+1;
}
}
//如果找到了合适的位置 就要用一般方法进行插入

for(j=i-1; j>=high+1; j--) {
S.r[j+1] = S.r[j];
}
S.r[high+1] = S.r[0];
}
}

算法特点:

  1. 稳定排序
  2. 只能用顺序结构,不能用于链式结构
  3. 适合初始记录无序,n较大时的情况
  4. 时间复杂度为O(n^2)
  5. 空间复杂度为O(1)

希尔排序

希尔排序:又称“缩小增量排序”,需要移动的数据量较少。

希尔排序实际上是采用分组插入的方法,将整个待排序的记录分割成几组,对每组进行直接插入排序。在增加每组的数据量,重新分组。

希尔排序的分组,不是简单地“逐段分割”,而是将相隔某个“增量”的记录分成一组

注意:最后一次分组一定要是“1”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void ShellSort(SqList &S) {
int k;
int dt[1000];
int i;
printf("请输入要做几次划分:");
scanf("%d",&k);
//对我们的划分进行初始化 最后一个一定要为1 是非递减的输入的
for(i=0; i<k; i++) {
printf("请输入第%d个划分大小",i+1);
scanf("%d",&dt[i]);
}
for(i=0; i<k; i++) {
ShellInsert(S,dt[i]);
}
}
void ShellInsert(SqList &S,int dk) {
//对整个数组机型查分排序
int i,j;
for(i=dk+1; i<=S.length; i++) {
//如果未知序列第一个元素比已经排好序的序列对应的元素小 就调换他们的位置
if(S.r[i]<S.r[i-dk]) {
S.r[0] = S.r[i];

//因为可能一个划分里有多个元素 因此需要进行循环
for(j=i-dk; j>0 && S.r[0]<S.r[j]; j-=dk) {
S.r[j+dk] = S.r[j];
}
//条出循环就说明找到了位置
S.r[j+dk] = S.r[0];
}
}
}

算法特点:

  1. 空间复杂度为O(1)
  2. 时间复杂度为P241 无法直接判断
  3. 算法不稳定
  4. 只能用顺序结构,不能用链式结构
  5. 最后一个增量只能是1
  6. 适合初始数据无序,n较大的情况

交换排序

交换排序的基本思想:两两比较带排序记录的关键字,一旦发现两个数据不和要求,就进行交换

交换排序有:冒泡排序和快速排序

冒泡排序

冒泡排序:是一种最简单的交换法,是相邻的数据进行交换。使最大,最小的数据跑到顺序表两端。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void BubbleSort(SqList &S) {
//冒泡排序就是通过多次的相邻两个元素的比较 交换 实现将最大或最小的元素放到两边
//不同的是 这个冒泡排序标记一个flag 如果在排序中没有出现交换 那么就证明排序已经玩成了 就不需要在继续执行了
int i;
int flag=1;
int m = S.length-1;
while((m>0)&&(flag==1)) {
//在进行循环之前将flag表为0 检测后续是否有数据变化
flag = 0;
for(i=1; i<m; i++) {
//我们执行的是从小到大排序
if(S.r[i+1]<S.r[i]) {
//交换两者位置 并且将flag立为0
S.r[0] = S.r[i+1];
S.r[i+1] = S.r[i];
S.r[i] = S.r[0];
flag = 1;
}
//最大值已经确定 不用再比较
--m;
}
}
}

算法特点:

  1. 稳定排序
  2. 可用于顺序存储,也可用于链式存储
  3. 移动此处较多,n较大时,此算法不是采用
  4. 时间复杂度为O(n^2)
  5. 空间复杂度为O(1)

快速排序

快速排序:解决了冒泡排序的只能相邻数据进行比较的缺点,能够实现非相邻的数据进行比较。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void FastSort(SqList &S,int left,int right) {
int i,j,t,temp;
if(left>right){
return ;
}
temp = S.r[left];
i = left;
j = right;
while(i!=j){
while(S.r[j]>=temp && i<j){
j--;
}
while(S.r[i]<=temp && i<j){
i++;
}
if(i<j){
t = S.r[i];
S.r[i]=S.r[j];
S.r[j] = t;
}
}
S.r[left] = S.r[i];
S.r[i] = temp;
FastSort(S,left,i-1);
FastSort(S,i+1,right);
return ;
}

算法特点:

  1. 算法是一个递归算法
  2. 时间复杂度为O(nlog2n)
  3. 空间复杂度为o(n)
  4. 排序方法是不稳定的
  5. 适用于顺序结构,不适合链式存储
  6. 适合于初始记录无序,n较大时的情况

选择排序

简单选择排序

简单选择排序:也称作直接选择排序

简单选择排序是我经常使用的 两个for循环就能实现

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void SelectSort(SqList &S){
//每次挑出一个最小值 放到指定的位置 再进行循环 纸质结束
int i;
int j;
int min;
int temp;
for(i=1;i<=S.length;i++){
min = i;
for(j=i;j<=S.length;j++){
if(S.r[j]<S.r[min]){
//找到最小的数
min = j;
}
}
if(min != i){
temp = S.r[i];
S.r[i] = S.r[min];
S.r[min] = temp;
}
}
}

算法特点:

  1. 时间复杂度O(n^2)
  2. 空间复杂度O(1)
  3. 既可用于顺序存储结构,也可用于链式存储结构
  4. 移动记录次数较少,每次机理占用的空间不多时可用,此时比直接插入排序快

第一个带图片的博客

为博客添加图片

步骤

将图片添加到图床

将图片添加到图床 能够实现将本地图片转化为网络图片

在添加![]()中使用网络图片地址

以下边的图片为例

天安门

图床链接

图床链接

抖音无水印视频获取(零门槛)

抖音视频无水印获取步骤

并没有写任何代码

需求

1.edge浏览器 开启开发者模式(能够获取网页信息)

2.网页运行抖音

步奏

1.用网页运行抖音

2.点击F12捕获网页信息

3.点击应用程序

4.点击媒体 如上图所示

5.点击媒体中的文件

6.点击下载 文件就下载了

C语言字符串

字符串

字符串常量

字符串常量是由一对双引号括起来的一个字符序列。如“hello”,“12354”“

无论双引号内是否包含字符,包含多少个字符,都代表一个字符串常量

为便于确定字符串的长度,C编译器会自动在字符串的末尾添加一个ASCLL码值为0的空操作符‘\0’作为字符串结束的标志,在字符串中可以不显示地写出

字符串:实际就是由若干有效字符构成且以字符‘\0’作为结束的一个字符序列

字符串的储存

  • C语言中没有提供字符串数据类型,因此字符串的存取要用字符型数组来实现
  • 判断存入字符型数组的是否是一个字符串需要通过观察其结尾是否有‘\0’来判断
  • 字符串结束标志‘\0’也占一个字节的内存,但他不计入字符串的实际长度,只计入数组的长度

字符串赋值时可以直接声明数组长度,也可以不声明数组长度

  • 当声明了数组长度时,不需要需要使输入的数组最后一位是‘\0’,系统会自动补’\0’
  • 没有声明数组长度,必须人为地在数组初始化列表中添加’\0’
1
2
3
char str[6] = {'H','e','l','l','o','\0'};
char str2[6] = {'H','e','l','l','o'};
char str3[] = {'H','e','l','l','o','\0'}

以上的三种赋值法都是合法的

字符串的长度

字符串的长度和字符串数组的长度不同

数组长度 = 字符数 + 1

二维字符数组

通常,我们将一个字符串放在一维字符数组中;将多个字符串放在多维字符数组中

字符第一维的长度代表字符串的个数,可以省略;但是第二维的长度不能省略,应该按最长的字符串的长度设定第二维的长度

1
2
3
char weekday[7][10] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
char weekday[][10] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

上述的两种初始化的方法是正确的

若字符串太长,无法写在一行中,则可将其拆分成几个小的片段写在不同的行中

1
2
char longString[] = "This is the first half of the string"
"and this is the second half.";

字符指针

字符指针:是指向字符型数据的指针变量。

字符指针中存储的地址值是字符数组的首地址

字符指针的定义

直接定义 char *ptr =”Hello”

间接定义 char *ptr; ptr = “Hello”

字符串的访问和输入/输出

如何访问字符串中的单个字符

和其他类型的数组一样,可以通过使用下标的方式访问存储在字符中的每个字符

str [0] 就是字符数组的第一个字符

此外可以通过字符指针间接访问存放于数组中的字符串

若字符指针ptr指向了数组str的首地址,既可以通过*(ptr+i)来引用字符串中的第i+1个数组;

*(ptr+i)相当于 *(str+i)即str[i]

注意,对于数组名str,不能使用str++操作使其指向字符串中的某个字符,因为数组名是一个地址常量,其值是不能被改变的

字符串的输入\输出

有三种方式可以对字符串进行输入或输出

第一种按照C格式符

1
2
3
4
5
6
7
8
for(i = 0; i < 10; i++)
{
scanf("%c",&str[i]); //输入
}
for(i = 0; i < 10; i++)
{
printf("%c",&str[i]); //输出
}

第二种按照S格式符

1
2
scanf("%s",str);
printf("%s",str);

注意当使用S格式符时,无论是输入还是输出,都不用&取地址符

因为此时str不仅仅是字符名,也是代表字符的首地址

第三种gets()puts()

使用字符串处理函数gets(),可以输入代空格的字符串

gets()以回车符作为字符串的终止符,同时将回车符从缓冲区读走,但不作为字符串的一部分

scanf()不读走回车符,回车符仍在输入缓冲区

puts()用于从括号内的参数给出的地址开始,依次输出存储单元中的字符,当遇到第一个’\0‘时输出结束,并自动输出一个换行符

printf()可以在输出行中添加一些其他的字符信息

由于gets()函数不能限制输入字符串的长度,需要用fgets()函数来约束它

1
fgets(name,sizeof(name),stdin)

字符串处理函数

  1. 函数功能 函数调用的一般形式 功能描述及其说明
    求字符串长度 strlen(str) 由函数值返回字符串str实际长度,即不包括’\0’在内的实际字符的个数
    字符串复制 strcpy(str1,str2) 将字符串str2复制到字符数组str1中,这里应确保字符数组str1的大小足以存放下字符串2
    字符串比较 strcmp(str1,str2) 比较字符串str1和str2的大小,结果分一下三种第一种当str1大于str2时,函数返回值大于0;当str1等于str2时,函数返回值等于0;当str1小于str2时,函数返回值小于0
    字符串链接 strcat(str1,str2) 将str2添加到str1的末尾
    “n族”字符串复制 strncpy(str1,str2,n) 将字符串str2的至多前n个字符复制到字符数组str1中
    “n族”字符串比较 strncmp(str1,str2,n) 比较前n个字符
    “n族”字符串链接 strncat(str1,str2,n) 将str2最多前n个字符添加到str1的末尾
  • Copyrights © 2015-2023 dwx
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信