开发者博客 – IT技术 尽在开发者博客

开发者博客 – 科技是第一生产力


  • 首页

  • 归档

  • 搜索

【Java基础】了解String String特性 创建字符

发表于 2021-11-20

String特性

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」

不可修改

本来想着说 String不可修改的原因是 String类用 final修饰,导致不可修改;

但是之后看了下 StringBuilder类和 StringBuffer类源码,这些可变字符串类也是用 final关键词修饰的,反应过来不是这个原因;

那 String不可以修改的原因是什么呢?

结论是:String本质上是一个 字符数组常量。

【问题】 为什么把 String设计成不可修改呢?(用 final修饰)

从性能和安全两个角度来可以解释这个问题

安全:String对象作为 Java类库中最常用到的参数,假如不是 final修饰成不可修改,可以被任意篡改,会很危险;

在多线程的情况下,String也是线程安全的。

性能:String用 final修饰下,String的 hash值属性是唯一的,可以放心的对字符串进行缓存,提高性能;

字符串常量池也是基于 String的不可变才可以实现的,一般情况下,创建一个字符串,是从常量池中找,省去了创建新的字符串对象,直接引用常量池的对象即可。

创建字符串的两种方法

1
2
ini复制代码String s1 = "hello"
String s2 = new String("hello");

一道经典的面试题

1
2
3
4
5
6
7
ini复制代码String a1 = new String("new");
String a2 = new String("new");
String b = "";
String c = "";
System.out.println(a1==a2);//false
System.out.println(b==c);//true
System.out.println(b.equals(c));//true

【问题】 两种字符串创建方式有什么区别?

使用""引号创建字符串:

  • 会去 方法区-字符串常量池中用 equals()方法找是否有 “”的字符串,
    • 如果有,则直接返回该字符串。
      • 如果没有,则在 方法区-字符串常量池中创建一个 “”的字符串。

使用new关键字创建字符串:

  1. 先去 方法区-字符串常量池中用 equals()方法找是否有 “new”的字符串,
    1. 如果有,继续下一步;
      1. 如果没有,则在 方法区-字符串常量池中创建一个 “new”的字符串,继续下一步;
  1. 再去 堆区创建一个 “new”的字符串对象,返回堆区对象的引用。

扩展知识:通过字符串的 intern()方法可以获取字符串常量池中字符的地址。

字符串拼接

字符串拼接在项目中很常用到,但是使用不当会造成效率低下;

1
ini复制代码String s1 = "a"+"b";

本质上是创建 StringBuilder对象,使用 append()方法拼接起来,最后用,如果在循环中使用 “+”拼接会创建大量的临时 StringBuilder对象,严重影响性能

1
2
3
4
ini复制代码StringBuilder s = new StringBuilder();
s.append("a");
s.append("b");
s1=s.toString();

在进行大量字符串拼接时,正确的做法是建议使用 StringBuilder或者 StringBuffer。

字符串常用 API

「删除字符串前后空格」

1
2
ini复制代码String s1 = " abc "
s1.trim();// "abc"

「字母大小写转换」

1
2
3
ini复制代码String s1 = "aB"
s1.toLowerCase("ab");
s1.toUpperCase("AB");

「判断两个字符串是否相等」

1
2
3
4
ini复制代码String s1 = "s1";
String s2 = "S1";
s1.equals(s2);//false
s1.equalslgnoreCase(s2)//true,忽略大小写进行比较

「判断字符的开始与结束」

1
2
3
4
5
ini复制代码String s1 = "Java";
String s2 = "Ja";
String s3 = "va";
s1.startWith(s2);//true
s1.endWith(s3);//true

「字符串分割」

1
2
ini复制代码String str = "www.baidu.com";
temp = str.split("."); // [www,baidu,com]

「判断是否符合正则规则」

1
2
3
4
5
6
7
8
9
10
11
csharp复制代码// 转自菜鸟
String Str = new String("www.runoob.com");

System.out.print("返回值 :" );
System.out.println(Str.matches("(.*)runoob(.*)"));

System.out.print("返回值 :" );
System.out.println(Str.matches("(.*)google(.*)"));

System.out.print("返回值 :" );
System.out.println(Str.matches("www(.*)"));

「字符串长度」

1
2
ini复制代码String s1 = "s1";
s1.length();//2

「子串查找」

1
2
3
csharp复制代码String lol = "lol";
System.out.println("lol.indexOf("l") = " + lol.indexOf("l"));//0
System.out.println("lol.indexOf("l") = " + lol.lastIndexOf("l"));//2

「字符串截取」

1
2
ini复制代码String num = "num";
num.substring(1);//"um"

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

☆打卡算法☆LeetCode 52、N皇后II 算法解析

发表于 2021-11-20

这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

推荐阅读

  • CSDN主页
  • GitHub开源地址
  • Unity3D插件分享
  • 简书地址
  • 我的个人博客
  • QQ群:1040082875

大家好,我是小魔龙,Unity3D软件工程师,VR、AR,虚拟仿真方向,不定时更新软件开发技巧,生活感悟,觉得有用记得一键三连哦。

一、题目

1、算法题目

“给定一个整数,返回N皇后问题的不同解决方案的数量。”

题目链接:

来源:力扣(LeetCode)

链接:52. N皇后 II - 力扣(LeetCode) (leetcode-cn.com)

2、题目描述

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。

image.png

1
2
3
4
ini复制代码示例 1:
输入: n = 4
输出: 2
解释: 如上图所示,4 皇后问题存在两个不同的解法。
1
2
3
ini复制代码示例 2:
输入: n = 1
输出: 1

二、解题

1、思路分析

这个题跟51题很像,是51题的升级款,51题是找到N皇后所有可能的解,这道题是只需要得到不同的解决方案的数量,那么就是只需要将所有可能的解改成得到可能的解的数量即可。

这道题还是用回溯法,在放置皇后的时候快速判断每个位置是否可以放置皇后。

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
csharp复制代码public class Solution {
public int TotalNQueens(int n)
{
var dp = new bool[n, n];
var res = 0;
FindP(0, 0);
return res;

void FindP(int y, int nums)
{
if (nums == n)
{
res++;
return;
}

for (int x = 0; x < n; x++)
{
if (CanP(x, y))
{
dp[y, x] = true;
FindP(y + 1, nums + 1);
dp[y, x] = false; // 撤销
}
}
}

bool CanP(int x, int y)
{
// 上
for (int i = y - 1; i >= 0; i--)
if (dp[i, x]) return false;
// 左上
for (int i = y - 1, j = x - 1; i >= 0 && j >= 0; i--, j--)
if (dp[i, j]) return false;
// 右上
for (int i = y - 1, j = x + 1; i >= 0 && j < n; i--, j++)
if (dp[i, j]) return false;

return true;
}
}
}

image.png

3、时间复杂度

时间复杂度 : O(N!)

其中N是皇后的数量。

空间复杂度: O(N)

其中N是皇后的数量。

三、总结

这道题非常经典。

总结来说就是一层层的搜索。

然后使用三个列表去标记每一层那些各自可以放置皇后。

然后找到解。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

指针这块必须牢牢把握!(初阶-上)

发表于 2021-11-20

这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战」


最近,想复习一下C语言,所以笔者将会在掘金每天更新一篇关于C语言的文章! 各位初学C语言的大一新生,以及想要复习C语言/C++知识的不要错过哦! 夯实基础,慢下来就是快!

指针是什么

在计算机科学中,指针(Pointer)是编程语言中的一个对象,利用地址,它的值直接指向(points to)存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针”。意思是通过它能找到以它为地址的内存单元。


内存
image.png


指针

指针是个变量,存放内存单元的地址(编号)。


1
2
3
4
5
6
7
8
arduino复制代码#include <stdio.h>
int main()
{
int a=10;//在内存中开辟一块空间
//这里我们对变量a,取出它的地址,可以使用&操作符。将a的地址存放在p变量中,p就 是一个之指针变量。
int*p= &a;
return 0;
}

总结:

  • 指针就是变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)。
  • 在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小就应该是4个字节。
  • 在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。

指针是用来存放地址的,地址是唯一标示一块地址空间的。

指针的大小在32位平台是4个字节,在64位平台是8个字节


寄存器

寄存器集成到CPU上


种类:

1
2
3
4
5
6
c复制代码eax
ebx
ecx
edx
ebp
esp

4.指针类型的意义

一.指针类型决定了指针解引用操作的时候,一次访问几个字节

(访问内存的大小)

image.png

1
2
3
4
5
6
7
8
c复制代码int main()
{
int a = 0x00001111;
char* pc = (char*)&a;
int* pi = &a;
printf("%d %d", *(pc), *(pi));// 17 4369
return 0;
}

二.指针类型决定了指针+-整数的步长(指针+-整数跳过几个字节)

1
2
3
4
5
6
7
8
c复制代码int main()
{
char arr[] = { 1,2,3,4,5,6 };
int* pi = (int*)arr;
char* pc = arr;
printf("%p %p\n", pi , pc );
printf("%p %p\n", pi+1,pc+1);
}

![image-20211116172610486](E:\Believe everything maybe true\Bit\C\初阶指针\初阶指针.assets\image-20211116172610486.png)

image.png
整形指针+1:跳过4个字节

字符指针+1:跳过1个字节

​ 指针+1跳过的字节数取决于指针指向的变量的类型


三.指针-指针得到的数字的绝对值是指针和指针之间元素的个数

例如:

1
2
3
4
5
6
c复制代码int main()
{
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("%d\n", &arr[9] - &arr[0]);//9
printf("%d\n", &arr[0] - &arr[9]);//-9
}

指针-指针的前提是两个指针指向的是同一块区域

如:&arr[9] - &ch[0] err

image.png

利用指针-指针求字符串长度(模拟strlen)

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
c复制代码//法1:计数器
size_t my_strlen(const char* str)
{
int count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
//方法2:递归
size_t my_strlen2(const char* str)
{
//如果str指向的不是\0,就加本身为1,然后递归下一个字符
if (*str != '\0')
return 1 + my_strlen(str + 1);
//当指向的是\0就返回0
else
return 0;
}
//方法3:指针-指针
size_t my_strlen3(const char* str)
{
char* start = str; //记录起始位置
char* end = str;
//遍历字符串找到\0位置
while (*end != '\0')
{
end++;
}
// \0位置-起始位置==>长度
return end - start;
}
int main()
{
char* str = "Struggling";
size_t ret = my_strlen1(str);
printf("%s的长度为%d\n", str, ret);
return 0;
}

指针-指针的标准规则

1
c复制代码允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较

image.png
p3:指向数组第一个元素前面的位置

p2:指向数组最后一个元素后面的位置

p1:指向数组元素的指针


今天就先到这吧~感谢你能看到这里!希望对你有所帮助!欢迎老铁们点个关注订阅这个专题! 同时欢迎大佬们批评指正!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Spring Boot 260正式发布:默认禁止循环依赖

发表于 2021-11-20

昨天,Spring官方正式发布了Spring Boot今年最后一个特性版本:2.6.0

同时,也宣布了2.4.x版本的终结。

那么这个新版本又带来了哪些新特性呢?下面就一起跟着DD来看看吧!

重要特性

  1. Servlet应用支持在 Cookie 中配置 SameSite 属性

该属性可通过server.session.cookie.same-site属性来配置,共有三个可选值:

  • Strict 严格模式,必须同站请求才能发送 cookie
  • Lax 宽松模式,安全的跨站请求可以发送 cookie
  • None 禁止 SameSite 限制,必须配合 Secure 一起使用

  1. 支持为主应用端口和管理端口配置健康组

这在 Kubernetes 等云服务环境中很有用。在这种环境下,出于安全目的,为执行器端点使用单独的管理端口是很常见的。拥有单独的端口可能会导致不可靠的健康检查,因为即使健康检查成功,主应用程序也可能无法正常工作。

以往传统的配置会将所有Actuator端点都放在一个单独的端口上,并将用于检测应用状态的健康组放在主端口的附加路径下。

  1. 增强/info端点,加入Java Runtime信息

增强后的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
json复制代码{
"java": {
"vendor": "BellSoft",
"version": "17",
"runtime": {
"name": "OpenJDK Runtime Environment",
"version": "17+35-LTS"
},
"jvm": {
"name": "OpenJDK 64-Bit Server VM",
"vendor": "BellSoft",
"version": "17+35-LTS"
}
}
}

该信息可以通过这个属性开启或关闭:

1
properties复制代码management.info.java.enabled=true
  1. 支持使用WebTestClient来测试Spring MVC

开发人员可以使用 WebTestClient 在模拟环境中测试 WebFlux 应用程序,或针对实时服务器测试任何 Spring Web 应用程序。

这次增强后,开发者可以在Mock环境中使用 @AutoConfigureMockMvc 注释的类,就可以轻松注入 WebTestClient。 这样编写测试就比之前容易多了。

  1. 增加spring-rabbit-stream的自动化配置

这次更新添加了 Spring AMQP 的新 spring-rabbit-stream 模块的自动配置。

当spring.rabbitmq.listener.type属性设置为stream时, StreamListenerContainer 是自动配置的。

spring.rabbitmq.stream.*属性可用于配置对broker的访问,spring.rabbitmq.listener.stream.native-listener 可用于启用native listener

  1. 支持/env端点和configprops配置属性的自定义脱敏

虽然 Spring Boot 之前已经可以处理 /env 和 /configprops 端点中存在的敏感值,只需要可以通过配置属性来控制即可。但还有一种情况,用户可能希望根据属性源自哪个 PropertySource 来应用清理。

例如,Spring Cloud Vault 使用 Vault 来存储加密值并将它们加载到 Spring 环境中。由于所有值都是加密的,因此将整个属性源中的每个键的值脱敏是有意义的。可以通过添加类型为 SanitizingFunction 的 @Bean 来配置此类自定义脱敏规则。

顺手推荐一下我一直在连载的免费教程:Spring Boot教程可以点击直达!。跟很多其他教程不同。这个教程不光兼顾了1.x和2.x版本。同时,对于每次的更新,都会选择一些相关内容修补Tips,所以对各种不同阶段的读者长期都会有一些收获。如果你觉得不错,记得转发支持一下!

其他变更

  1. Reactive Session 个性化

当前版本可以动态配置 reactive session 的有效期

1
ini复制代码server.reactive.session.timeout=30
  1. Redis 链接自动配置链接池

当应用依赖中包含 commons-pool2.jar 会自动配置 redis 链接池 (Jedis Lettuce 都支持)。如果你想关闭则通过如下属性:

1
2
3
ini复制代码spring.redis.jedis.pool.enabled=false

spring.redis.lettuce.pool.enabled=false
  1. 构建信息个性化

  • 通过 spring-boot-maven-plugin 支持自动生成此次构建信息的 build-info.properties
1
2
3
4
5
6
7
8
9
xml复制代码    <plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludeInfoProperties>
<excludeInfoProperty>version</excludeInfoProperty>
</excludeInfoProperties>
</configuration>
</plugin>
  1. Metrics新增指标

应用启动的两个新指标:

1
2
3
lua复制代码application.started.time: 启动应用程序所需的时间

application.ready.time: 启动应用到对外提供服务所需时间

磁盘空间的两个指标:

1
2
3
c复制代码disk.free: 磁盘空闲空间

disk.total: 磁盘总空间
  1. Docker镜像的构建

增强docker-maven-plugin插件的功能:

  • 为自定义镜像设置tags标签
  • 网络配置参数,可用于Cloud Native Buildpacks的构建过程
  • 支持使用 buildCache 和 launchCache 配置参数自定义用于缓存层的名称,这些层由构建包提供给构建的镜像
  1. 移除 2.4 版本中的过期属性

由于2.4版本完成历史使命,因此有大量过期属性被移除,最近要升级的小伙伴一定要关注一下这部分内容,因为你原来的配置会失效!

关于Spring MVC 和 servlet 部分属性:

旧属性(已删除) 新属性
spring.web.locale spring.mvc.locale
spring.web.locale-resolver spring.mvc.locale-resolver
spring.web.resources.* spring.resources.*
management.server.base-path management.server.servlet.context-path

关于Elasticsearch属性的变更:

因为内容较多,这里就不完全贴出来了,有兴趣的可以看看文末参考资料中的官方信息。

  1. 默认情况完全禁止Bean的循环引用

还记得前几天我发布的这篇:为什么IDEA不推荐你使用@Autowired ?

对于鼓励大家用构造器的方式,还受到了一些网友的嘲讽。

那么在2.6.0之后,如果小伙伴依然觉得循环依赖无所谓,还坚持要用下面的这种模式:

那么,你将收获下面这样的报错:

1
2
3
4
5
6
7
8
9
10
vbnet复制代码┌─────┐
| a (field private com.example.demo.B com.example.demo.A.b)
↑ ↓
| b (field private com.example.demo.A com.example.demo.B.a)
└─────┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.

其实,Spring官方这样做,也是为了鼓励大家养成不要有循环依赖的好习惯。

但对于屎山项目,可能这样的要求对于开发者会很痛苦。所以,你也可以通过下面的配置,放开不允许循环依赖的要求:

1
ini复制代码spring.main.allow-circular-references=true
  1. SpringMVC 默认路径匹配策略

Spring MVC 处理程序映射匹配请求路径的默认策略已从 AntPathMatcher 更改为PathPatternParser。

Actuator端点现在也使用基于 PathPattern 的 URL 匹配。需要注意的是,Actuator端点的路径匹配策略无法通过配置属性进行配置。

如果需要将默认切换回 AntPathMatcher,可以将 spring.mvc.pathmatch.matching-strategy 设置为 ant-path-matcher,比如下面这样:

1
ini复制代码spring.mvc.pathmatch.matching-strategy=ant-path-matcher

好了,关于Spring Boot 2.6的版本解析到这里结束了。

最后,再推荐一下我一直在连载的免费教程:Spring Boot教程可以点击直达!

跟很多其他教程不同。这个教程不光兼顾了1.x和2.x版本。同时,对于每次的更新,都会选择一些相关内容修补Tips,所以对各种不同阶段的读者长期都会有一些收获。如果你觉得不错,记得转发支持一下!

参考资料

  • spring.io/blog/2021/1…
  • github.com/spring-proj…
  • www.oschina.net/news/169783…

好了,今天的学习就到这里!如果您学习过程中如遇困难?可以加入我们超高质量的Spring技术交流群,参与交流与讨论,更好的学习与进步!更多Spring Boot教程可以点击直达!,欢迎收藏与转发支持!

欢迎关注我的公众号:程序猿DD,分享外面看不到的干货与思考!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

省市县三级联动的实现方案 1 问题的来源 2 解决方案

发表于 2021-11-20

这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

  1. 问题的来源

  1. 在开发中经常需要进行一个页面级联效果,最经常使用莫过于省市级三级联动的效果,比如选完北京市然后把北京市下属的区丰台区,海淀区,昌平区…返回前端
  1. 解决方案

本次解决方案仅涉及后端(ps:主要是前后端分离的项目写多,不会前端操作了)

2.1 数据库设计

1
2
3
4
5
6
7
8
java复制代码CREATE TABLE `sys_china`  (
`Id` int(11) NOT NULL COMMENT '主键id',
`Name` varchar(40) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '名称',
`Pid` int(11) NULL DEFAULT NULL,
`status` varchar(2) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '1' COMMENT '1:启用,2:禁用',
PRIMARY KEY (`Id`) USING BTREE,
INDEX `FK_CHINA_REFERENCE_CHINA`(`Pid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COMMENT = '省市区表' ;

2.2 后端代码(springboot+mysql)

代码地址:
github.com/Dr-Water/Le…
sql代码也在项目的sql文件夹中
在这里插入图片描述
数据库一共四个字段:
在这里插入图片描述

列 含义
id 主键id
name 省市县的名称
pid 本条记录父类id
status 本条记录的启用状态

2.3 操作测试步骤

将项目启动起来使用postman进行测试:
请求路径:localhost:8010/link/city/queryById

  1. 前端第一次请求的时候id传成0 先把全国的省和直辖市查出来,
    在这里插入图片描述
  2. 拿到省或者直辖市的id再去请求接口这样就能将本省的下的市或者区全查出来
    在这里插入图片描述

2.4数据的存储以及回显

1.在使用的时候最好将省市县的主键id 和 name都存到数据库中
2. 前端回显的时候可以直接拿name去展示省市县的名称,也可以使用id从数据库中请求
3. 当数据量特别大的时候,如果使用id去请求数据库可能会对数据库造成很大的压力,这个时候可以将所有的省市县全查出来放到redis中,减轻数据库的压力
4. 如果值列表的数据不是很大,可以直接将数据以id为key,name 为value 的大map的形式全给前端, 前端回显的时候使用id去map中去匹配值

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

Python的从0到1(第十四天)-Python的条件判断1

发表于 2021-11-20

这是我参与11月更文挑战的第19天,活动详情查看:2021最后一次更文挑战

我们在使用无线耳机听音乐的时候,我们可以设置轻点两下耳机就播放下一首。如果我只设置轻点两下耳机,就播放下一首,那么,由于缺少正在使用耳机(耳机塞进耳朵视为正在使用)的前提条件,无论你是否正在使用,只要轻点两下,就会播放下一首。

所以,我们的设置应该是这样:轻点两下,如果耳机正在使用中,就播放下一首。

而这个设置中的如果…就…逻辑,就是与计算机沟通的逻辑—条件判断,其作用就是明确的让计算机知道:在什么条件下,该去做什么。

对于Python也是一样的,Python之所以可以完成自动化任务,比如Python爬虫项目中可以成功获取我们指定的数据信息,就是因为它可以执行条件判断。

接下来,我们通过中国新四大发明之一的“扫码支付”来感受一下逻辑判断的趣味性。

二维码支付听起来似乎是一项十分新鲜的技术,其实,这个跟手机报差不多,算不上新颖的技术。早在上世纪90年代,二维码支付技术就已经形成,只不过在国内是近几年刚刚兴起的。

2014年春节微信红包上线,为扫码支付奠定了用户基础。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
python复制代码
import random
print("请输入红包金额,最多两位小数")

money=round(float(input()),2)
print("请输入红包数量")
number=int(input())
print("请输入红包方式:1.拼手气红包 2.普通红包 3.专属红包")
mode=int(input())
money=int(money*100)
if mode == 1:
for i in range(1, number):
a = random.randint(1, money-(number-i)) # 随机生成红包金额
money = money-a
print("第" + str(i) + "个人,收到" + str(a/100) + "元,")
print("第" + str(number) + "个人,收到" + str(money/100) + "元,")
elif mode == 2:
for i in range(1, number+1): # 限制人数
print("第" + str(i) + "个人,收到" + str(round((money/100)/number,2)) + "元")
else:
print("指定人获取"+str(money/100)+"元")

你看到没?这就是条件判断如果…就…逻辑产生的魔法。那么,这个条件判断的代码,要怎么码呢?

条件判断

在Python宇宙,条件判断语句总共有三种表现形式,我们先从最简单的单向判断:if开始说起:

单向判断:if

单向判断:if代表什么呢?我们知道在发红包的时候,如果专属红包超过200元,就会提示单个红包金额不可超过200元。

你会发现,上面的示例中,有一个如果…就。那么,我们用代码翻译这段话,就可以用if来表述,下面,请你来运行一下下方代码。

1
2
3
4
5
6
7
8
9
10
python复制代码# 为单个红包面额
money = 201

# 条件:如果专属模式单个红包超过200元

if money > 200:

# 结果:显示‘单个红包金额不可超过200元’的结果

print('单个红包金额不可超过200元')

那么,这段代码是如何实现的呢?

首先,第一行代码,用赋值运算符=对当前情况进行赋值:将红包面额201赋值给变量—红包面额

第二步,确定if条件:如果变量money的值>200时,就执行冒号后,下一行的命令。

第三步,用print()命令打印出结果:单个红包金额不可超过200元。所以,if语句的单向判断逻辑,我们可以这样归纳:

在这里,你可能注意到了一个细节:在条件判断代码中的冒号 : 后、下一行内容的前面,会空几个格,但这是为什么呢?

首先,在计算机的沟通语言中,空格的学名叫缩进,比如我们写文章都要空两个格,这就叫首行缩进。
对于Python而言,冒号和缩进是一种语法。它会帮助Python区分代码之间的层次,理解条件执行的逻辑及先后顺序。【注:缩进是四个空格或一个Tab键】

并且,在if条件语言中,缩进不需要我们手动按空格键。当你用英文输入法打:后按回车,我们的开发工具(用来编写Python代码的程序)为了方便大家编程,会自动实现下一行代码,向右缩进的功能。

此时,被缩进的内容(print()函数)和if条件语句组成了一个代码块(一个整体),成为了if条件下的内部命令。

这意味着:如果赋值满足if条件,计算机就会准确执行if条件内部的命令(即缩进的代码块)。

那么下面,请你运行下面这个代码,看看运行结果是什么。

1
2
3
4
5
6
7
8
9
10
python复制代码# 为单个红包面额
money = 201

# 条件:如果专属模式单个红包超过200元

if money > 200:

# 结果:显示‘单个红包金额不可超过200元’的结果

print('单个红包金额不可超过200元')

执行上述代码,控制台提示错误:IndentationError: expected an indented block

翻译一下就是缩进错误:期望一个缩进块

这是因为,当我们去掉缩进时,if条件和print命令成为了两个不同的代码组,属于平行关系。你看:if条件下面,缺少了可以执行的动作。我们想一下:无论条件成不成立,都不会执行什么操作,这个条件的存在没有任何意义。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

SSIS学习使用十三:SSIS变量概述:变量的介绍使用、断点

发表于 2021-11-20

这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战

翻译参考

本文主要参考翻译自 The Stairway to Integration Services 系列文章的 原文An Overview of SSIS Variables – Step 13 of the Stairway to Integration Services,目的在于对 SSIS 有一个全面清晰的认识,所有内容在原文的基础上进行实操,由于版本差异、个人疑问等多种原因,未采用完全翻译的原则,同时也会对文中内容进行适当修改,希望最终可以更有利于学习和了解 SSIS,

感谢支持!


本篇将演示 SSIS变量 部分、变量配置和通过表达式管理动态值。在之前许多练习中我们使用了SSIS变量,但是并没有给与太多的关注。下面将重点介绍下SSIS变量。

新建SSIS项目

打开原本的解决方案。

点击顶部菜单栏中的文件——新建——项目。然后在”商业智能”中选择 Integration Services,新建一个 Integration Services项目。

如下,项目名称命名为My_Second_SSIS_Project,同时选择“创建新解决方案”。根据需要指定位置。

点击“确定”创建。

重命名SSIS包 Package.dtsx 为 VariablesAndParameters.dtsx,如下:

从变量开始(Beginning with Variables)

变量的使用和操作

右键”控制流”画布的空白处,选择“变量”。打开 “变量”(Variables) 窗口

第一个按钮是”添加变量”(Add Variable),点击并新建一个变量,重命名为MyVariable。

注意变量的 作用域(Scope) 是SSIS包的名字:VariablesAndParameters。一个好的类比是作用域相当于容器(containers)。

如下,有三种作用域:

  1. Task
  2. Container
  3. Package

Tasks, Containers 和 Packages 是可执行文件(或可执行的)。可执行文件是SSIS中某种类型的对象,拥有属性并可以引发事件。

一个任务总是位于一个容器中(A task always resides within a Container)。“等等,安迪;如果只是将 ‘执行SQL任务’ 放在控制流上是什么情况?”很好的问题:包(Package)也是一个容器。

SSIS公开其他容器,即 序列容器(Sequence Container),For循环容器(For Loop Container) 和 Foreach循环容器(Foreach Loop Container)。这些容器都可以容纳 任务(task),并且每种类型的 容器(如包) 也都是可执行文件。

SSIS提供其他方式来可视化作用域(scope)。为了演示,从SSIS工具箱添加一个序列容器到控制流,然后添加一个 “执行SQL任务” 到序列容器,如下

包资源管理器(Package Explorer) 在树视图控件中描绘了 作用域(scope)。表示的含义为:包(VariablesAndParameters)包含一个容器(序列容器),该容器又包含一个任务(执行SQL任务)

树状视图可帮助我们形象地说明在作用域内“上”或“下”的意思。从“执行SQL任务”的角度来看,“序列容器”位于其“上方”。如果从序列容器开始,则“执行SQL任务”的作用域是“在其下方”或“包含在其中”。

指出 “执行包任务”(Execute Package Task) 包含在 VariablesAndParameters 包中在技术上是正确的,但是该语句忽略了“序列容器”(Sequence Container),并提供了包与任务之间关系的不完整描述。为了更加准确,可以这样声明:“ VariablesAndParameters包 包含一个序列容器,其中包含一个执行SQL任务。”

变量的作用域由变量所作用的可执行文件定义。我们名为 MyVariable 的变量的作用域为 VariablesAndParameters(或包) 作用域。有时可以将这些变量称为包作用域变量。

关于 “包资源管理器”(Package Explorer) 中的树视图的另一点是:我认为这是对所谓的“执行堆栈”(Execution Stack)的公平表示。我更愿使用术语“执行树”(Execution Tree),但该术语已经在SSIS中的其他地方被使用。

执行堆栈是指可执行文件之间的关系。可执行文件的执行通常将堆栈“向下”从包流到容器再到任务。一些消息(例如在“高级日志记录”中观察到的事件)在堆栈中“向上移动”,可以对照参考第12部分介绍的高级日志中的事件。正如可以从那些示例中了解到的那样,Package Explorer可以让我们受益匪浅。

SSIS变量 在 SSIS 2012 中默认为包作用域。可以更改 SSIS变量 的作用域,但是到目前为止,还没有可以在包(package)级别以下设置变量作用域的好用例。

通过单击“变量”窗口工具栏中的第二个按钮“移动变量”,可以将变量移至另一个作用域,如图所示:

选择 MyVariable 变量,然后点击 “移动变量”(Move Variable) 按钮,显示选择新的作用域窗口。

如上图,我们将看到一个树状视图,其中显示了在 VariablesAndParameters 包中可用的作用域 —— 可执行文件。你可以选择所需的范围,然后单击“确定”按钮。出于演示目的,选择“序列容器”,然后单击“确定”。

请注意,MyVariable 的范围已更改为 “序列容器”,如图所示:

在“控制流”中,单击“序列容器”及其子可行性程序。 MyVariable才会在“变量”窗口中可见。这是因为 SSIS的默认行为是仅显示当前作用域或更高作用域的变量。

可能你会想“我可以改变这个行为吗?” 当然,通过点击变量窗口工具栏中的第四个按钮 —— 网格选项(Grid Options),你可以改变这个行为。

“变量网格选项”窗口包含“变量”窗口的许多选项。我们要选中标记为 “显示所有作用域的变量” 的复选框,如图所示:

点击”确定”按钮,返回控制流和变量窗口。点击控制流的空白区域,此时你仍然能看到变量窗口中的 MyVariable 变量 —— 尽管它位于包的下面(位于序列容器作用域)。

关于依作用域而隐藏变量的小测试

注:变量依据执行堆栈从低向上获取的

我不是很认同因为作用域而隐藏变量。

为什么我不同意隐藏执行堆栈之下或之外的变量?如果我不小心在低于包的作用域内创建了一个变量,则可以创建第二个变量来进行补偿。允许两个变量在不同范围内具有相同的名称,但它们不是相同的变量。

为了演示,删除“执行SQL任务”,在它的位置添加一个“脚本任务”(Script Task)。创建一个新的名为 “MyVariable” 的变量,其作用域自动为包作用域。

为了演示目的,此处故意为变量提供了不同的的默认值。

双击“脚本任务”,打开编辑器。设置脚本语言属性为 “Microsoft Visual C# 2010”。点击 “ReadOnlyVariables” 属性的值文本框中的省略号。当选择变量窗口显示时,注意仅仅一个 MyVariable 变量列出来。

选择 “MyVariable” 变量并点击OK按钮。

点击脚本任务编辑器中的 “编辑脚本”(Edit Script) 按钮,打开 VSTAProjects 脚本编辑器,找到 Main() 函数。在 “Add your code here” 注释的下面,添加一行代码:

1
cs复制代码MessageBox.Show(Dts.Variables["User::MyVariable"].Value.ToString());

关闭 VSTAProject 脚本编辑器,并点击 “脚本任务编辑器” 的”确定”按钮。

按F5在调试器中执行SSIS包。消息框将会显示”MyVariable”变量的值。显示的是哪个值?

如果你从一开始就跟着做,则会显示“序列容器”中提供的MyVariable的默认值。为什么这个变量赢了?

考虑一下我前面提到的”执行堆栈”,并考虑事件如何从原始可执行文件(任务,容器或包——task, container, or package)遍历堆栈。还记得这是如何工作的吗?事件向上传送,也就是事件冒泡。

变量的行为类似。在执行脚本任务之前,SSIS尝试锁定(lock)MyVariable变量。 “锁定它,安迪?”是的,将其锁定。 “为什么?”很好的问题。

请考虑一下,如果两个可执行文件试图同时使用同一变量,会发生什么情况。如果其中一个可执行文件正在写入变量值,而另一个可执行文件仅读取值该怎么办?在读取过程中,很可能希望该变量的值保持静态。为确保变量值的确保持静态(或确定性(deterministic)),SSIS在变量上设置了锁,因此在可执行文件使用过程中该值不能被无意间操纵。

思考变量在SSIS中如何锁定的一种方法是想象锁定机制(在此类比中为“钥匙持有者”)轮询执行堆栈,用以搜索名为 MyVariable 的变量。

钥匙持有者从”脚本任务”开始,并询问它:“脚本任务,你是否有一个名为MyVariable的变量?”脚本任务回答“否”。因此,钥匙持有者进入上一个执行堆栈,并询问”序列容器”。“序列容器,你是否有一个名为MyVariable的变量?”序列容器回答“是”。钥匙持有者停止查找。它找到了它想要的变量。

如果我的变量是隐藏的(在SSIS的所有版本中都是默认值),则我可能没有意识到我有两个名为MyVariable的变量在不同的作用域。我可能不小心将MyVariable的作用域设置为序列容器而忘了它,然后在以后创建了一个包作用域的MyVariable(想想“我是否已经创建了此变量?”)。

此外,“显示所有作用域的变量”复选框 的状态不会作为解决方案或项目属性的一部分永久保存。当我关闭 SQL Server 数据工具并重新打开它时,该设置将恢复为默认值。

这就是为什么我不喜欢在不同范围内隐藏变量的默认行为的原因:无法通过脚本任务访问名为 MyVariable 的包作用域变量。当我选择 ReadOnlyVariables 时,它不会出现在我的列表中。我无法指定 VariablesAndParameters.User::MyVariable。该选项根本不存在。而且,除非启用了“在所有范围内显示变量”,否则我不知道该变量是否存在,因为它不是默认值。

如果默认行为是将所有变量都设置为包作用域,则默认也应为在所有作用域中显示变量。

变量数据类型(Variable Data Types)

SSIS变量有几种可用的数据类型:各种数字数据类型,日期,字节,布尔值,字符串和字符。也许最有趣的是 Object 数据类型:

SSIS变量的Object数据类型的可以保存各种值,包括标量,例如单个整数,字符串和日期。Object也可以容纳对象。例如集合,数组,记录集和数据集(collections, arrays, recordsets and datasets)。此处不会进行详细介绍和练习,但是鼓励您阅读并练习我写的一篇文章中的示例,名为:SSIS 101: Object Variables, ResultSets, and Foreach Loop Containers。

变量值(Variable Values)

“变量” 窗口的 “值” 列显示变量的默认值。

作为最佳实践,即使将SSIS包部署到生产环境中,我也将默认值设置为用于开发 SSIS 包的值。做出此决定的原因有很多,我们将在以后的文章中介绍更多内容;但是主要原因是,如果在生产包执行过程中发生“不好的事情”,或者产生了一个错误,我宁愿用生产数据加载我的开发环境,而不是用开发数据加载我的生产环境。

如下显示了到目前为止我们使用的两个变量的默认值:

变量表达式(Variable Expressions)

SSIS表达式语言很难学习。对我来说,它看起来像是花括号语言(C#,Java等)和 Transact-SQL 的混合体。如果你认为 SSIS 被定位为企业数据集成平台,则这非常适合。

我们可以使用表达式来操纵 SSIS 变量的值。为了演示,在MyVariable 的 “表达式”(Expression) 文本框中单击省略号。

“表达式生成器”(Expression Builder) 窗口显示并包含四个区域:变量和参数(Variables and Parameters),表达式语言函数和运算符(Expression Language functions and operators),表达式(Expression)和计算结果值(Evaluated Value)。

如图所示:

“变量和参数”树视图包含用于表达式的所有可用变量和参数的列表。由于变量位于SSIS包,“变量和参数”虚拟文件夹中的唯一变量是包作用域的 MyVariable ,如图所示:

为什么我们看不到序列容器作用域的MyVariable?因为 序列容器 作用域的 MyVariable 是我们正在设置 “表达式”列 的变量。

我们如何确定?可以将变量从“变量和参数”拖动到“表达式”文本框中,然后单击 “计算表达式”(Evaluate Expression) 按钮,如图所示:

记住,包作用域的变量 MyVariable 的默认值为0。 “序列容器”作用域的 MyVariable 的默认值为42。因此,这是包作用域内的MyVariable。

表达式语言树 视图包含SSIS表达式语言 运算符和函数,

从表达式文本框中删除 “@[User::MyVariable]”。从 “日期/时间函数”(Date/Time Functions) 节点下拖拽 “DATEPART”函数 到 表达式文本框。

在“表达式”文本框中,用 "ss"(双引号包括) 覆盖<<datepart>>。在 SSIS表达式语言树 视图中,单击 GETDATE()函数 并将其拖到 “表达式”文本框 中。将鼠标悬停在<<date>>占位符上,然后将GETDATE()函数放到<<date>>占位符上。表达式应该为 DATEPART("ss",GETDATE()),如下:

该表达式会将当前日期和时间中的秒数放入“序列容器”作用域MyVariable值中。关闭Expression Builder,并注意变量窗口。

注意变量名称左侧的 fx(“x的函数”,“x的f”或“f sub x”)图标。这表示表达式正在控制变量的值。

我们配置的表达式出现在“变量”窗口的“表达式”列中。

为了演示,请按 F5键 以在 SSIS调试器 中启动包。脚本任务将显示一个消息框,其中包含 MyVariable 变量的值。它是介于0和59之间的数字,是当前日期和时间的秒数,如图所示:

很酷的东西,那让我们开始实践吧!

在开始讨论其他内容之前,让我们先整理一下。如果 SSIS包 正在SSDT中运行,请停止调试器。从序列容器中删除”脚本任务”。删除两个 MyVariable 变量。

使用变量建立动态的连接(Building Connections Using Variables)

让我们把已经学会的综合起来。变量可用于构建其他变量值。

让我们通过使用 变量 来 创建 包含一些数据的文本文件的 路径 来演示这一点。

通过创建下面的名为 Songs.csv 的文本文件开始:

1
2
3
4
ini复制代码Id,Artist,Song
"0","Waylon Jennings","Lonesome, On'ry, and Mean"
"1","Willie Nelson","Blue Eyes Cryin' in the Rain"
"2","Kris Kristofferson","Sunday Mornin', Coming Down"

添加 平面文件源适配器

在 SQL Server 数据工具(SSDT)中,将数据流任务添加到控制流,并将 优先约束 从 序列容器 连接到 新 数据流,如下:

打开数据流任务并添加一个 文本文件源(平面文件源Flat File Source)适配器 到界面。

打开 平面文件源编辑器,并通过单击 “平面文件连接管理器” 下拉菜单右侧的 “新建” 按钮来创建一个新的 平面文件连接管理器,如图所示:

显示 平面文件连接管理器编辑器 后,将 “连接管理器名称”(Connection manager name) 属性设置为 “Songs Flat File”,并将 “文件名”(File name)属性 设置为上面保存的文件的位置。

在 “文本限定符”(Text qualifier)属性 中添加双引号,如图所示:

英文界面:

点击”确定”按钮,关闭 平面文件连接管理器编辑器。可能需要点击点一下左侧的”列”(Columns) 才能点击 “确定”

注意 Songs Flat File 现在出现在 “平面文件连接管理器”(Flat file connection manager) 属性的下拉框中。

点击”确定”,关闭平面文件源编辑器。

创建数据库TestDB

可能你在 Microsoft SQL Server 实例中没有名为 “TestDB” 的数据库,你可以使用任何数据库进行测试或开发,也可以使用下面的 T-SQL 脚本创建自己的 TestDB:

1
2
3
4
5
6
7
8
9
10
11
12
13
sql复制代码Use master
go
If Not Exists(Select name
From sys.databases
Where name = 'TestDB')
begin
print 'Creating TestDB'
Create Database TestDB
print 'TestDB created'
end
Else
print 'TestDB already exists'
go

关于幂等(幂等脚本可以类比幂等函数、幂等接口、幂等操作)

上面是一个幂等脚本的示例。

现在我要坦白一下:我不知道这个词是什么意思。就像我所知道的许多事情一样,我是从 Jamie Thomson(博客| @jamiet) 学到的。但是 杰米(Jamie) 解释说,它意味着可以重复执行代码。如果你的实例和SQL Server中都没有名为TestDB的数据库,并且运行了上面的脚本两次,它将在第一次执行脚本时创建该数据库,并在第二次(及后续)执行时通知该数据库已经存在。

这里有两点:

  1. 幂等脚本是安全且有用的;
  2. 杰米(Jamie)真的很聪明。

幂等(idempotent、idempotence)

在编程中一个幂等操作的特点是其任意 ****。幂等函数,或幂等方法,是指多次执行所产生的影响均与一次执行的影响相同可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。

OLE DB 源适配器

配置连接

返回 SSDT,拖拽一个 OLE DB 源适配器到 数据流 界面,在 平面文件源 和 OLE DB目标 之间连接一个数据流路径。如下:

打开 OLE DB目标编辑器,点击 “OLE DB连接管理器”(OLE DB connection manager) 下拉框右侧的 “新建” 按钮,创建一个新的 OLE DB连接管理器,打开 配置OLE DB连接管理器 窗口。

点击 “新建” 按钮,显示 “连接管理器编辑器”(Connection Manager editor)。在 “服务器名”(Server name) 属性下拉框中输入 SQL Server服务器名和实例名,在 “连接到一个数据库”(Connect to a database) 下拉框中输入数据库名,如下:

点击”确定”,关闭 连接管理器编辑器 窗口。配置OLE DB连接管理器窗口显示新的连接,如下:

在 “数据连接”(Data connections) 列表中选择刚才新建的连接,并点击”确定”按钮。OLE DB目标编辑器中,“OLE DB连接管理器”属性下拉列表中显示了选择的 连接管理器 的名称。

单击 “表或视图的名称” 属性下拉列表右侧的 “新建” 按钮,显示 “创建表” 窗口。该窗口显示了根据数据流中的 “元数据”(metadata) 构建的 数据库定义语言(DDL) 语句。

表的名称是从 OLE DB目标适配器 的名称派生的 —— 将其更改为”Songs”。列的定义是从 “平面文件源” 和 “OLE DB目标” 之间的数据流路径派生的:

对表名进行编辑后,单击“确定”按钮。注意,单击“确定”按钮将对你的数据库执行此DDL语句。这就是为什么表格显示在 “表格或视图的名称” 属性下拉列表中的原因,如图所示:

在”OLE DB目标编辑器”的底部,有警告信息提示。

上面显示的警告消息告诉我们,下一步是 映射“映射页面” 上的列(map the columns on the Mappings page)。

但请注意,“确定”按钮是启用的。这是 SSIS 2012 中的新功能。也是 Microsoft称为“灵活创作顺序”(Flexible Order of Authoring) 或 FOA 的一个示例。

我对FOA感触良多。我不喜欢”OLE DB目标”。为什么?现在,我可以单击“确定”按钮,并在数据流中保留半配置的(partially-configured,部分配置的)”OLE DB目标”。在以前的版本中,这是不可能的,因为直到执行映射后(我们将在一分钟内完成)之前,“确定”按钮都是被禁用的。

但是,当我配置 “查找转换”(Lookup transformations) 时,我真的很喜欢FOA。”查找转换” 有五个页面,当发生悲剧性事件时(例如由于 Unicode 和 非Unicode 字符串导致映射失败,请耐心等待……),我讨厌必须记住在以前的界面中所做的所有操作。 FOA给予,FOA拿走。(FOA gives and FOA takes away.)

返回SSDT,单击 “OLE DB目标编辑器” 上的 “映射” 页。执行此操作时,将发生自动映射,如图所示:

自动映射

你可能会想,“什么是自动映射?” 很高兴你这样问!可用的输入列表示从连接到 OLE DB目标 输入的数据流路径流入 OLE DB目标 的数据的架构。可用目标列表示在配置为目标的表或视图中的可用的列。在当前例子中,我们之前创建了目标表 —— OLE DB目标从哪里获取元数据来创建该表?

要回答此问题,请首先单击“确定”按钮,以完成 OLE DB目标适配器 的配置。接下来,右键单击将 平面文件源适配器 连接到 OLE DB目标适配器 的 数据流路径,然后单击 “编辑” 以打开 “数据流路径编辑器”。单击“元数据”页面以显示 数据流路径 的结构属性,如图所示:

看起来熟悉吗?我认为这看起来像表定义,有列名,数据类型和长度。当我们单击“新建”按钮以在”OLE DB目标编辑器”中创建新表时,此元数据提供了架构信息。

由于 “可用目标列” 中的列(上上图)是根据 “可用输入列” 的 元数据 构建的,因此这些列匹配名称和数据类型,并且字段名称和数据类型的这种对齐允许发生自动映射。

(this alignment of field name and data type allows auto-mapping to occur)

单击”确定”按钮,以关闭数据流路径编辑器。

表达式中的变量(Variables in Expressions)

打开 “SSIS变量” 窗口,并添加一个名为 FileDirectory 的新的包作用域的 String 变量。将FileDirectory 变量的 Value 属性设置为存储 Songs.csv文件的文件夹;

创建另一个名为 FileName 的包作用域的String变量。将 FileName 变量的 Value 属性设置为“Songs.csv”;

再添加一个包作用域的String变量,命名为FilePath。如图所示:

点击”FilePath”变量”表达式”(Expression)的省略号,打开 “表达式生成器”(Expression Builder) 窗口。展开左上部分的 “变量和参数”(Variables and Parameters) 虚拟文件夹。

从 变量和参数树 视图中点击和拖拽 “User::FileDirectory” 到 “表达式” 文本框中。

在变量名称(“@[User::FileDirectory]”)之后,添加一个空格,后跟加号(“+”)。加号充当字符串的串联运算符。然后再添加 反斜杠字符串 和 加号”+”。单击 User::FileName 变量并将其拖到 Expression 文本框中,如图所示:

点击 计算表达式Evaluate Expression 按钮,查看表达式的值:

backslash 反斜杠。此处连接反斜杠时使用的是两个反斜杠。原因在于反斜杠是转义字符(escape character——A backslash plus another character indicates some special text)。反斜杠加上另一个字符表示一些特殊文本。单独使用"\"将会报错。

单击”确定”按钮,关闭表达式生成器。

动态属性表达式中的变量(Variables in Dynamic Property Expressions)

现在,我们有一个变量 —— FilePath —— 包含源文件的完整路径。FilePath 变量上的表达式从其他两个变量构造此路径:FileDirectory和FileName。

让我们使用这个完整路径将 平面文件连接管理器(名为“Songs Flat File”) 动态定向 到该文件。单击 Songs Flat File平面文件连接管理器,然后按F4键以显示连接管理器属性(如图所示):

单击“表达式”值文本框中的省略号,以显示“属性表达式编辑器”。单击Property下拉列表,然后选择 ConnectionString 属性,如图所示:

单击 ConnectionString 属性旁边的 Expression 文本框中的省略号,以显示 Expression Builder。在“变量和参数”树视图中展开 “变量和参数” 虚拟文件夹。将 FilePath 变量拖到 Expression文本框 中,如图所示:

单击“确定”按钮,关闭 “表达式生成器” 窗口,然后单击“确定”按钮,关闭“属性表达式编辑器”。

如果在“属性”窗口中展开 Expressions 属性集合,则可以查看 ConnectionString 属性,如图所示:

介绍断点和变量状态(Introducing Breakpoints and Variable State)

在SSIS调试器执行过程中,断点很方便进行故障排除(或仅查看状态)。如本系列中的 “控制流任务错误处理“、”高级日志记录“ 中所述,SSIS使用事件/侦听器模式(event/listener pattern)。事件处理程序、日志记录、“进度/执行结果”选项卡 和 断点 都是处理事件侦听器的例子。

Event handlers, logging, the Progress / Execution Results tab, and breakpoints are examples of event listeners.

在本节中,我们设置一个断点,以在数据流任务引发 PreExecute 事件时暂停执行。

在运行测试执行之前,返回 “控制流” 并右键单击 “数据流任务”,然后单击 “编辑断点…”(Edit Breakpoints),如图所示:

当 “设置断点——数据流任务”(Set Breakpoints – Data Flow Task) 窗口显示后,点击 “当容器接收到OnPreExecute事件时断开”(Break when the container receives the OnPreExecute event) 截断条件。

在 “数据流任务”(Data Flow Task) 引发 PreExecute事件 时启用断点,将导致数据流任务在执行之前暂停执行。

单击“确定”按钮,以关闭“设置断点-数据流任务”窗口。

“数据流任务” 上出现一个 栗色 的点,表明已设置断点:

注,上上图中”数据流任务”显示有红叉错误。应该在无错误状态下配置和测试执行。此处演示的红叉在点击 “数据流任务” 中的 “OLE DB目标” 打开其编辑器后,仅再点击一次确定,就消失了。

通过单击”play”按钮,或按F5来启动调试器。

包验证错误。数据流任务 OLE DB 目标 [22]: 由于为列“Id”指定了多个代码页(1252 和 936),无法处理此列。

执行 SSIS 时报错”包验证错误”。

如下,在执行时遇到了包验证的错误

1
sh复制代码错误	1	验证错误。数据流任务 OLE DB 目标 [22]: 由于为列“Id”指定了多个代码页(1252 和 936),无法处理此列。  	VariablesAndParameters.dtsx	0	0

936为汉语代码,1252为拉丁文代码。用哪个代码要在 “平面文件连接管理器编辑器” 中设置。一般中文版系统(和SQL Server)中,会默认选择中文,此时就会产生错误。

之前配置时选择的区域设置和代码页码为:英语(美国)和1252。最开始选择它的原因是:文本文件Songs.csv中的内容都是英文和字符。

区域设置和代码页码的正确设置应该是:中文(简体,中国)和936。即和Windows系统的区域设置一致。

如下,编辑 “Songs Flat File”平面文件连接管理器:

和系统设置一致,中文-936

测试执行包

修改好后,重新执行 SSIS 包。

debug执行

执行 SSIS包 操作会在 “数据流任务” 引发 PreExecute事件 时,在 “数据流任务” 处运行会暂停,这由 断点指示器(栗色的点)中出现的 黄色箭头 指示,如图所示:

要查看 FilePath 变量的状态,请在菜单上单击”调试”(DEBUG),将鼠标悬停在 “窗口”(Windows)上 ,然后单击 “局部变量”(Locals) 窗口,如图所示。

注意,你还可以按住Ctrl和Alt键,然后按V再按L打开 “局部变量”(Locals) 窗口。以这种方式按多个键有时被称为 和弦(a chord)。

显示后,在“局部变量”窗口中展开 “变量”节点(Variables)。滚动直到找到 User::FilePath 变量,如图所示:

“值”(Value) 字段包含 SSIS包 的调试执行到此时的 变量值。请注意,该值在 FilePath 和 FileDirectory 变量值字段中将反斜杠表示为双反斜杠。

在调试器中继续执行SSIS包,按F5键或单击“继续(Play)”按钮。包执行完成,我们从 Songs.csv 文件加载三行,如图所示:

检查执行结果

上面执行完成后。可以返回SSMS,查看加载到 Songs 表中的数据。如下:

可以看到已正确导入。

此处列出对比的原因是,可以对照查看 “平面文件连接管理器” 或 “平面文件源” 中的列的预览效果:

可以看到这些预览是不完整的。必须以实际测试效果为准。

总结

本篇,我们演示了SSIS变量、变量配置和通过表达式动态管理值的多个方面。

We demonstrated several facets of SSIS variables, variable configuration, and dynamic value management via expressions.

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

「Linux 奏章 11」磁盘管理

发表于 2021-11-20

「这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战」。

  1. Linux 磁盘管理指令

Linux 磁盘管理常用三个命令为 df、du 和 fdisk

  • df:列出文件系统的整体磁盘使用量
  • du:检查磁盘空间使用量
  • fdisk:用于磁盘分区

1.1 df [查询系统整体磁盘情况]

df (disk usage) 命令参数功能:检查文件系统的磁盘空间占用情况。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息。

语法:

1
shell复制代码$ df [-ahikHTm] [目录或文件名]

选项与参数:

  • -a :列出所有的文件系统,包括系统特有的 /proc 等文件系统;
  • -h :以人们较易阅读的 GBytes, MBytes, KBytes 等格式自行显示;
  • -T :显示文件系统类型, 连同该 partition 的 filesystem 名称 (例如 ext3) 也列出;
  • -i :不用硬盘容量,而以 inode 的数量来显示

实例 1:将系统内所有的文件系统列出来!

1
shell复制代码[root@localhost ~]# df

实例 2: 将容量结果以易读的容量格式显示出来

1
shell复制代码[root@localhost ~]# df -h

实例 3: 将系统内的所有特殊文件格式及名称都列出来

1
shell复制代码[root@localhost ~]# df -aT

实例 4:将 /etc 底下的可用的磁盘容量以易读的容量格式显示

1
shell复制代码[root@localhost ~]# df -h /etc

1.2 du [查询指定目录磁盘情况]

du (disk free) 命令也是查看使用空间的,但是与 df 命令不同的是 du 命令是对文件和目录磁盘使用的空间的查看,还是和 df 命令有一些区别的。

语法:

1
shell复制代码$ du [-ahskm] <文件或目录名称>

选项与参数:

  • -a :列出所有的文件与目录容量,因为默认仅统计目录底下的文件量而已。
  • -h :以人们较易读的容量格式 (G/M) 显示;
  • -s : 指定目录占用大小
  • -h : 带计量单位
  • --max-depth=1 : 列出的子目录深度
  • -c : 列出明细的同时,增加汇总值

实例 1:只列出当前目录下的所有文件夹容量(包括隐藏文件夹)

1
2
3
4
5
6
shell复制代码[root@localhost ~]# du
8 ./test4 <==每个目录都会列出来
8 ./test2
....中间省略....
12 ./.gconfd <==包括隐藏文件的目录
220 . <==这个目录(.)所占用的总量

⭐直接输入 du 没有加任何选项时,则 du 会分析当前所在目录的文件与目录所占用的硬盘空间。

实例 2:将文件的容量也列出来

1
2
3
4
5
6
7
8
shell复制代码[root@localhost ~]# du -a
12 ./install.log.syslog <==有文件的列表了
8 ./.bash_logout
8 ./test4
8 ./test2
....中间省略....
12 ./.gconfd
220 .

实例 3:检查根目录底下每个目录所占用的容量

1
2
3
4
5
6
7
8
9
shell复制代码[root@localhost ~]# du -sm /*
7 /bin
6 /boot
.....中间省略....
0 /proc
.....中间省略....
1 /tmp
3859 /usr <==系统初期最大就是他了啦!
77 /var

通配符 * 来代表每个目录。


⭐df vs du:

  • du 是通过搜索文件来计算每个文件的大小然后累加,du 能看到的文件只是一些当前存在的,没有被删除的。
  • df 通过文件系统来快速获取空间大小的信息,当我们删除一个文件的时候,这个文件不是马上就在文件系统当中消失了,而是暂时消失了,当所有程序都不用时,才会根据 OS 的规则释放掉已经删除的文件,df 记录的是通过文件系统获取到的文件的大小,比 du 强的地方就是能够看到已经删除的文件,而且计算大小的时候,把这一部分的空间也加上了,更精确了。
  • 当文件系统也确定删除了该文件后,这时候 du 与 df 就一致了。

1.3 fdisk [磁盘分区]

fdisk 是 Linux 的磁盘分区表操作工具。

语法:

1
shell复制代码$ fdisk [-l] 装置名称

选项与参数:

  • -l :输出后面接的装置所有的分区内容。若仅有 fdisk -l 时, 则系统将会把整个系统内能够搜寻到的装置的分区均列出来。

实例 1:列出所有分区信息

1
shell复制代码[root@localhost tmp]# fdisk -l

实例 2:找出你系统中的根目录所在磁盘,并查阅该硬盘内的相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
shell复制代码[root@localhost ~]# df /            <==注意:重点在找出磁盘文件名而已
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/hdc2 9920624 3823168 5585388 41% /

[root@localhost ~]# fdisk /dev/hdc <==仔细看,不要加上数字喔!
The number of cylinders for this disk is set to 5005.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
(e.g., DOS FDISK, OS/2 FDISK)

Command (m for help): <==等待你的输入!

输入 m 后,就会看到底下这些命令介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
shell复制代码Command (m for help): m   <== 输入 m 后,就会看到底下这些命令介绍
Command action
a toggle a bootable flag
b edit bsd disklabel
c toggle the dos compatibility flag
d delete a partition <==删除一个partition
l list known partition types
m print this menu
n add a new partition <==新增一个partition!
o create a new empty DOS partition table
p print the partition table <==在屏幕上显示分割表
q quit without saving changes <==不储存离开fdisk程序
s create a new empty Sun disklabel
t change a partition's system id
u change display/entry units
v verify the partition table
w write table to disk and exit <==将刚刚的动作写入分割表
x extra functionality (experts only)

离开 fdisk 时按下 q,那么所有的动作都不会生效!相反的, 按下 w 就是动作生效的意思。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
shell复制代码Command (m for help): p  <== 这里可以输出目前磁盘的状态

Disk /dev/hdc: 41.1 GB, 41174138880 bytes <==这个磁盘的文件名与容量
255 heads, 63 sectors/track, 5005 cylinders <==磁头、扇区与磁柱大小
Units = cylinders of 16065 * 512 = 8225280 bytes <==每个磁柱的大小

Device Boot Start End Blocks Id System
/dev/hdc1 * 1 13 104391 83 Linux
/dev/hdc2 14 1288 10241437+ 83 Linux
/dev/hdc3 1289 1925 5116702+ 83 Linux
/dev/hdc4 1926 5005 24740100 5 Extended
/dev/hdc5 1926 2052 1020096 82 Linux swap / Solaris
# 装置文件名 启动区否 开始磁柱 结束磁柱 1K大小容量 磁盘分区槽内的系统

Command (m for help): q

想要不储存离开吗?按下 q 就对了!不要随便按 w 啊!

使用 p 可以列出目前这颗磁盘的分割表信息,这个信息的上半部在显示整体磁盘的状态。

1.4 mkfs [磁盘格式化]

磁盘分割完毕后自然就是要进行文件系统的格式化,格式化的命令非常的简单,使用 mkfs(make filesystem) 命令。

语法:

1
shell复制代码$ mkfs [-t 文件系统格式] 装置文件名

选项与参数:

  • -t :可以接文件系统格式,例如 ext3, ext2, vfat 等(系统有支持才会生效)

实例 1:查看 mkfs 支持的文件格式

1
2
shell复制代码[root@localhost ~]# mkfs[tab][tab]
mkfs mkfs.cramfs mkfs.ext2 mkfs.ext3 mkfs.msdos mkfs.vfat

按下两个[tab],会发现 mkfs 支持的文件格式如上所示。

实例 2:将分区 /dev/hdc6(可指定你自己的分区) 格式化为 ext3 文件系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
shell复制代码[root@localhost ~]# mkfs -t ext3 /dev/hdc6
mke2fs 1.39 (29-May-2006)
Filesystem label= <==这里指的是分割槽的名称(label)
OS type: Linux
Block size=4096 (log=2) <==block 的大小配置为 4K
Fragment size=4096 (log=2)
251392 inodes, 502023 blocks <==由此配置决定的inode/block数量
25101 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=515899392
16 block groups
32768 blocks per group, 32768 fragments per group
15712 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912

Writing inode tables: done
Creating journal (8192 blocks): done <==有日志记录
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 34 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
# 这样就创建起来我们所需要的 Ext3 文件系统了!简单明了!

1.5 fsck [磁盘检验]

fsck(file system check)用来检查和维护不一致的文件系统。

若系统掉电或磁盘发生问题,可利用 fsck 命令对文件系统进行检查。

语法:

1
shell复制代码$ fsck [-t 文件系统] [-ACay] 装置名称

选项与参数:

  • -t : 给定档案系统的型式,若在 /etc/fstab 中已有定义或 kernel 本身已支援的则不需加上此参数
  • -s : 依序一个一个地执行 fsck 的指令来检查
  • -A : 对/etc/fstab 中所有列出来的 分区(partition)做检查
  • -C : 显示完整的检查进度
  • -d : 打印出 e2fsck 的 debug 结果
  • -p : 同时有 -A 条件时,同时有多个 fsck 的检查一起执行
  • -R : 同时有 -A 条件时,省略 / 不检查
  • -V : 详细显示模式
  • -a : 如果检查有错则自动修复
  • -r : 如果检查有错则由使用者回答是否修复
  • -y : 选项指定检测每个文件是自动输入yes,在不确定那些是不正常的时候,可以执行 # fsck -y 全部检查修复。

实例 1:查看系统有多少文件系统支持的 fsck 命令

1
2
shell复制代码[root@localhost ~]# fsck[tab][tab]
fsck fsck.cramfs fsck.ext2 fsck.ext3 fsck.msdos fsck.vfat

实例 2:强制检测 /dev/hdc6 分区:

1
2
3
4
5
6
7
8
9
shell复制代码[root@localhost ~]# fsck -C -f -t ext3 /dev/hdc6 
fsck 1.39 (29-May-2006)
e2fsck 1.39 (29-May-2006)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
vbird_logical: 11/251968 files (9.1% non-contiguous), 36926/1004046 blocks

如果没有加上 -f 的选项,则由于这个文件系统不曾出现问题,检查的经过非常快速!若加上 -f 强制检查,才会一项一项的显示过程。

1.6 mount & umount [磁盘挂载与卸载]

Linux 的磁盘挂载使用 mount 命令,卸载使用 umount 命令。

1.6.1 mount

1
shell复制代码$ mount [-t 文件系统] [-L Label名] [-o 额外选项] [-n]  <装置文件名>  <挂载点>

实例:用默认的方式,将刚刚创建的 /dev/hdc6 挂载到 /mnt/hdc6 上面!

1
2
3
4
5
6
shell复制代码[root@localhost ~]# mkdir /mnt/hdc6
[root@localhost ~]# mount /dev/hdc6 /mnt/hdc6
[root@localhost ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
.....中间省略.....
/dev/hdc6 1976312 42072 1833836 3% /mnt/hdc6

1.6.2 umount

1
shell复制代码$ umount [-fn] 装置文件名或挂载点
  • -f :强制卸除!可用在类似网络文件系统 (NFS) 无法读取到的情况下;
  • -n :不升级 /etc/mtab 情况下卸除。

实例:卸载 /dev/hdc6

1
shell复制代码[root@localhost ~]# umount /dev/hdc6

1.7 lsblk [查看设备挂载情况]

lsblk :

lsblk -f :

  1. Linux 磁盘分区示例⭐

2.1 预备知识

  • Linux 采用了一种叫 “挂载” 的处理方法,它的整个文件系统中包含了一整套的文件和目录,且将一个分区和一个目录联系起来。
  • ⭐挂载 mount 的是分区,不是磁盘,一个磁盘含多个分区。
  • Linux 常见的两种磁盘:IDE (hda..)、SCSI (sda..),目前主要使用 SCSI 硬盘。
  • 查看所有设备挂载情况:lsblk 或 lsblk -f

2.2 虚拟机增加硬盘并分区后挂载的经典实例⭐

2.2.1 添加磁盘

① 虚拟机添加硬盘,重启后生效,lsblk -f 查看!

2.2.2 磁盘分区【fdisk】

② 分区命令 : fdisk /dev/sdb

  • m:显示命令列表
  • p:显示磁盘分区,像 fdisk -l 一样
  • n:新增分区
  • d:删除分区
  • w:写入并退出

2.2.3 磁盘格式化【mkfs】

③ 格式化 : mkfs -t ext4 /dev/sdb1 或 mkfs.ext4 /dev/sdb1

2.2.4 磁盘挂载【mount】

④ 挂载

  • mount /dev/sdb1 /newdisk
  • umount <设备名称或挂载目录> : umount /dev/sdb1 、umount /newdisk
  • 注意:使用命令行的 mount 挂载分区,重启后会失效!
  • 永久挂载:通过修改 /etc/fstab 实现永久挂载,添加完成后,执行 mount -a 立即生效!

⑤ 设置可以自动挂载:编辑 /etc/fstab 文件 (新增如图内容),永久挂载!

2.2.5 查询磁盘占用情况【df、du】

  • df -h:查询系统整体磁盘使用情况

  • du -h <目录>:查询指定目录的磁盘占用情况

  1. 工作实用指令⭐

  • 统计 /opt 文件夹下文件的个数 : ls -l /opt | grep "^-" | wc -l
  • 统计 /opt 文件夹下目录的个数 : ls -l /opt | grep "^d" | wc -l

  • 统计 /opt 文件夹下文件的个数,包括子文件夹里的 : ls -lR /opt | grep "^-" | wc -l
  • 统计 /opt 文件夹下目录的个数,包括子文件夹里的 : ls -lR /opt | grep "^d" | wc -l

  • 以树状显示目录结构 : tree /opt (安装 tree, yum install tree)

希望本文对你有所帮助🧠

欢迎在评论区留下你的看法🌊,我们一起讨论与分享🔥

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

EasyC++33,引用与const

发表于 2021-11-20

大家好,我是梁唐。

这是EasyC++系列的第33篇,引用与const。

想要追求更好阅读体验的同学,可以点击访问github仓库: EasyLeetCode。

引用与const

前文当中说过,我们可以让函数接收一个引用变量,从而免去变量拷贝的开销,达到提升程序运行效率的目的。

如果我们想要传递引用,但又不希望在函数内部对引用的变量进行修改,以免影响外部变量。我们可以使用常量引用,也就是加上const修饰符。

1
C++复制代码double sqrt(const double &x);

由于我们加上了const修饰符,当我们在函数内部对引用进行修改的时候,会触发编译器的报错。一般来说,如果传递的只是基本类型的变量,我们其实没有必要这么操作,直接值传递即可。这种做法一般用在传递一些大型结构体或者是大型容器的时候。

这里有一个小细节需要当心,由于我们传递的是引用,需要保证传递的参数是一个实参,而不是表达式。如这样的代码编译时会报错:

1
2
3
4
5
6
7
8
C++复制代码double distance(double &x, double &y) {
return sqrt(x * x + y * y);
}

int main() {
double x = 3.0, y = 4.0;
cout << distance(x + 3.0, y + 4.0);
}

报错的原因在于,函数distance接收的是一个double类型的引用,而我们传递的却是x+3这样的表达式。显然表达式没有对应的引用。所以编译器会报错,告诉我们参数类型不匹配:

但神奇的是,如果我们把函数签名稍微改一下,加上const修饰符,会发现报错消失了:

1
2
3
C++复制代码double distance(const double &x, const double &y) {
return sqrt(x * x + y * y);
}

这并不是编译器的bug,而是编译器针对const引用做了特殊处理。当编译器发现传入的不是double类型的变量的时候,它会创建一个临时的无名变量,将这个临时变量初始化成x+3.0,然后再传入这个临时变量的引用。C++只会对const引用参数执行这个操作。

除了表达式之外,如果变量的类型不匹配也一样会创建临时变量。这些临时变量只会在函数调用期间存在,函数运行结束之后,编译器会将其删除。

为什么会有这样的设计呢?C++ Primer当中提供了这样一个例子:

1
2
3
4
5
6
7
8
C++复制代码void swapr(int &a, int &b) {
int temp = b;
b = a;
a = temp;
}

long a = 3, b = 5;
swapr(a, b);

在早期C++没有严格限制的情况下,这段代码会发生什么呢?

由于类型不匹配,所以编译器会创建两个临时的int变量,但它们初始化成3和5,再传入函数当中。然后执行函数当中交换变量的逻辑,但问题是,我们交换的是两个临时变量,原变量并不会生效。

所以后来版本的C++优化了这个问题,禁止了传递引用时创建临时变量。而当引用有const修饰时并不会对原值进行修改,并不会影响逻辑和结果,所以豁免了这个禁令。

const修饰符的优点

在函数签名当中,如果要接收引用,我们要尽可能使用const,我们来看下这样做的好处:

  • 可以避免无意中修改数据
  • 可以处理const和非const参数,否则,只能接受非const变量
  • 可以接受临时变量

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

C语言学习笔记---结构体的定义和初始化

发表于 2021-11-20

「这是我参与11月更文挑战的第20天,活动详情查看:2021最后一次更文挑战」。

  在C语言中结构体通常用来将不同类型的变量打包在一起,方便对对象的统一管理。

结构体定义

  要使用结构体时,首先要进行结构体的声明。声明的形式如下:

1
2
3
c复制代码struct 结构体名 {
结构体所包含的变量
};

  比如现在要描述一本书的标题,作者,价格。声明格式如下:

1
2
3
4
5
c复制代码struct book{
char title[30];
char author[30];
float value;
};

  用字符数组来存储书的标题和作者,用浮点型来存储书的价格。声明完成之后,此时在内存中并没有分配存储空间。因为结构体也是一种数据类型,它和char、int、float等类型一样属于数据类型。

  声明完结构体之后,就告诉了计算机,我要使用的数据类型是结构体类型。在后面操作这个对象的时候,就按照结构体的方式来操作。此时就相当于只写了一个 int ,并没有定义变量。

  下面开始定义结构变量。

1
c复制代码struct book b1;

  此时就在内存中创建了一个结构类型的变量b1,系统就会在内存中给b1分配一个存储空间。

  在上面将结构体的声明和变量的定义分成了两部分,也可以使用一起实现。

1
2
3
4
5
c复制代码struct book{
char title[30];
char author[30];
float value;
} b1;

  用这种方式就可以声明一个结构类型,同时定义了一个结构变量b1。如果还想在定义一个变量b2,可以在初始化的时候定义。也可以单独定义。

  单独定义:

1
c复制代码struct book b2;

  在声明的时候时候定义

1
2
3
4
5
c复制代码struct book{
char title[30];
char author[30];
float value;
} b1,b2;

  如果这个结构体不需要再定义其他的变量,那么在声明时可以省略结构体名,比如上面的代码就可以简写为:

1
2
3
4
5
c复制代码struct {
char title[30];
char author[30];
float value;
} b1,b2;

  将结构体名book直接省略掉,但是这样的话,以后如果再想单独定义一个变量b3就不能实现了。

结构体初始化

方法一:

  初始化上面定义的两个变量,在定义的时候直接初始化。

1
2
3
4
5
6
7
c复制代码   struct book{
char title[30];
char author[30];
float value;
};
struct book b1 = {"语文","张三",19.8};
struct book b2 = {"数学","李四",21.3};

  初始化的时候,各个项之间用逗号隔开,按照定义时的顺序,依次填入各个成员的值。

方法二:

  可以每个成员单独初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
c复制代码struct book{
char *title;
char *author;
float value;
} b1,b2;

b1.value = 19.8;
b1.title = "语文";
b1.author = "张三";

b2.title = "数学";
b2.value = 21.3;
b2.author = "李四";

  定义好变量之后,给每个成员单独初始化,初始化的时候可以不按照顺序来。在单独初始化的时候,这里要注意一下,需要将结构体声明中的的字符数组改为指针。如果还是用字符数组,那么在给数组赋值的时候,只能使用下标,一位一位的给数组中每个元素赋值。而不能将数组定义好之后,然后给数组的首地址一个字符串。字符数组只有在定义的时候,可以直接用字符串赋值。如下所示:

1
2
3
4
c复制代码    char str[30] = "123";  //正确

char str1[30];
str1 = "abc"; //错误

  如果需要给成员单独初始化,就需要将申明中的数组改为指针,如果不想修改的话,那么也可以在定义的时候,直接初始化。

方法三:

  在初始化的时候,可以直接使用结构成员运算符—点(.)访问结构体中的成员。这样的话就可以直接指定要向哪个成员赋值。可以不按照顺序来对成员进行赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
c复制代码struct book{
char title[30];
char author[30];
float value;
} ;

struct book b1={
.value = 19.8,
.title = "语文",
.author = "张三"
};

struct book b2={
.title = "数学",
.value = 21.3,
.author = "李四"
};

  在操作结构体中的对象时,也可以使用点(.)来访问。比如需要打印结构成员的信息时,可以使用下面的代码:

1
2
c复制代码	printf("%s %s %f\r\n",b1.author,b1.title,b1.value);
printf("%s %s %f\r\n",b2.author,b2.title,b2.value);

在这里插入图片描述

  在结构体变量后加一个点就可以直接访问变量中的具体成员。成员的操作方法和它对应的类型操作方法一样。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

1…270271272…956

开发者博客

9558 日志
1953 标签
RSS
© 2025 开发者博客
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%