【Linux初阶】基础IO - 文件操作(使用系统接口实现) | vim批量注释代码
🌟hello,各位读者大大们你们好呀🌟
🍭🍭系列专栏:【Linux初阶】
✒️✒️本篇内容:重新理解文件和文件操作,C语言实现的简单文件操作,文本初始权限,系统接口介绍(open、close、write、read)
🚢🚢作者简介:计算机海洋的新进船长一枚,请多多指教( •̀֊•́ ) ̖́-
文章目录
- 前言
- 一、重新谈论文件
- 二、重新谈论文件操作
- 1.除了C/C++,其他语言有文件接口吗?
- 2.vim知识补充 - 如何批量注释代码
- 3.C语言实现的简单文件操作
- (1)写入文件
- (2)读取文件
- (3)追加文件内容
- 三、文本初始权限
- 四、系统接口介绍
- 1.open接口
- 2.open接口中的flag标记位
- (1)flag标记位介绍
- (2)flag底层原理(仅作了解)
- 3.close
- 4.write接口
- 5.read接口
- 6.库函数接口和系统调用接口的关系
- 结语
前言
相信很多朋友在C语言学习阶段已经接触过文件操作的相关知识,在这篇文章中,我们会更深入的学习文件的知识,以操作系统的视角,重新认识和理解文件。
那么话不多数,让我们直接开始吧!
一、重新谈论文件
在学习文件之前,我们需要重新回顾一下文件的相关知识,让我们对文件有一个系统、立体的认识。
- 空文件,也要占据空间;
文件 = 内容 + 属性
;- 文件操作 = 对内容/属性 or 内容+属性的操作;
- 标定一个文件,必须使用:文件路径+文件名【唯一性】;
- 如果没有指明对应的文件路径,默认是在当前路径下进行文件访问;
- 当我们把 fopen,fclose,fread,fwrite等接口写好,代码编译完形成二进制可执行文件之后,如果没有运行起来,文件对应的操作不会被执行;
文件操作的本质:进程对文件的操作
; - 一个文件要被访问,就必须先被打开(用户进程调用接口,OS帮我们打开);
所以,我们研究文件操作,实际上就是在研究进程和被打开文件的关系
二、重新谈论文件操作
1.除了C/C++,其他语言有文件接口吗?
实际上,除了C/C++,Java、Python、php、go等语言都有自己的文件操作接口,它们的文件操作接口都不一样!那么问题来了,有那么多的文件操作接口,我们应该怎么降低我们的学习成本呢?
在解答上面这个问题之前,我们要明确我们的文件是如何被操作的。我们的文件储存在我们的磁盘中,磁盘 -> 硬件 -> OS(硬件被操作系统管理)->所有人想访问磁盘都不能绕过OS -> 使用OS提供的接口(文件级接口)。
所以无论上层语言怎么变化,a.库函数底层必须使用系统调用接口;b.库函数可以千变万化,但是底层不变。这就得出了我们降低学习成本问题的答案,只要我们学习不变的东西,我们就可以降低我们学习文件操作知识的成本了。在这篇文章中,我会着重讲述文件操作的底层原理和常见系统调用接口
。
———— 我是一条知识分割线 ————
2.vim知识补充 - 如何批量注释代码
在博主使用 vim进行学习的过程中,常常需要对其中的代码进行大范围操作,经过网络搜索发现各种阅读量靠前的信息并不能满足自己对 vim操作快速便捷简明的要求,因此在此处根据自己的编码习惯对 vim相应的知识做相应的补充。
批量化注释代码:Ctrl+v
进入块选择模式(V-BLOCK模式),h、j、k、l
分别代表左下上右
,控制块的大小,输入大写 I
,此时下方会提示进入“insert”模式,再输入注释符//
,按Esc回到初始NORMAL模式
(返回后才会完成批量化注释)。
撤销上一次的操作:初始NORMAL模式下按 u。
取消批量注释:Ctrl+v进入块选择模式(V-BLOCK模式),选中你要删除的行首的注释符号,注意// 要选中两个,输入d 即可完成取消批量注释。
调整代码格式 + 批量删除代码:Ctrl+v进入块选择模式(V-BLOCK模式),下拉选中你要删除的代码首行,输入小写 d
,代码集体向前缩进一行
。输入大写 D
,删除选中行所有的代码
。
———— 我是一条知识分割线 ————
3.C语言实现的简单文件操作
(1)写入文件
打开文件的操作如下,fopen(要打开的文件名, 操作形式),其中操作形式有 r(读)、w(写)、r+、w+、a(追加文件内容)、a+ 等。
//r,w, r+(读写,不存在出错),w+(读写, 不存在创建), a(append, 追加), a+()【追加式写入】FILE* fp = fopen(FILE_NAME, "w");
在下面的演示代码中,我们以 w为例,即文件不存在,创建+写入。以 w单纯打开文件,c会自动清空文件内部的数据。
#include <stdio.h>
#include <unistd.h>// 我没有指明路径
#define FILE_NAME "log.txt"int mian()
{FILE* fp = fopen(FILE_NAME, "w"); //r,w, r+(读写,不存在出错),w+(读写, 不存在创建), a(append, 追加), a+()if (NULL == fp){perror("fopen"); //验证文件打开是否成功return 1;}int cnt = 5;while (cnt){fprintf(fp, "%s:%d\n", "hello world", cnt--);}fclose(fp);}
最后,我们会把五个hello world写入到文件中
【补充】我们可以通过 cat + 文件名指令打印对应的文件内容
(2)读取文件
fget读取文件:以行为单位,从特定的文件流中获取数据,放到 s指向的缓冲区之中,如果成功返回 s,失败返回NUll。
在下面的演示代码中,我们以 r操作为例,即文件存在,读取文件。
FILE* fp = fopen(FILE_NAME, "r");
代码如下(示例):
#include <stdio.h>
#include <unistd.h>
#include <string.h>// 我没有指明路径
#define FILE_NAME "log.txt"int mian()
{FILE* fp = fopen(FILE_NAME, "r"); //r,w, r+(读写,不存在出错),w+(读写, 不存在创建), a(append, 追加), a+()if (NULL == fp){perror("fopen"); //验证文件打开是否成功return 1;}char buffer[64];while(fgets(buffer, sizeof(buffer) - 1, fp) != NULL)//#include <string.h>,//fgets会把数组最后的\0(换行)也输入获取进去,所以要-1{buffer[strlen(buffer) - 1] = 0;//输入指令时需要按回车,回车也会被也输入获取进去,所以要将最后一位置0puts(buffer);}fclose(fp);}
代码运行成功之后,会输出文件内容
(3)追加文件内容
我们以 a操作
为例,即文件存在,追加文件内容。
#include <stdio.h>
#include <unistd.h>
#include <string.h>// 我没有指明路径
#define FILE_NAME "log.txt"int mian()
{FILE* fp = fopen(FILE_NAME, "r"); //r,w, r+(读写,不存在出错),w+(读写, 不存在创建), a(append, 追加), a+()if (NULL == fp){perror("fopen"); //验证文件打开是否成功return 1;}int cnt = 5;while (cnt){fprintf(fp, "%s:%d\n", "hello world", cnt--);}fclose(fp);}
代码每成功运行一次,会向文件中追加5个hello world的内容
【注意】
其他语言会有其他的文件操作方法/接口,以C++为例,会有 std::ifsteam::opean()打开文件、std::ifsteam::ifsteam(传文件流)等,有兴趣的小伙伴可以自己去了解一下哦~
三、文本初始权限
文件创建的初始权限为666,但是需要0666 & ~umask,umask的默认值为0002,因此我们普通文本类文件创建时大部分的权限都是664
。
664的权限表示为: -rw-rw-r–
四、系统接口介绍
在上面的章节中,我们学习的C语言进行文件操作的部分接口,但这并不是我们本次学习最重要的知识,下面通过对系统调用接口的学习,相信大家一定能对文件的底层有更深入的了解。
1.open接口
opean,打开或创建一个文件,我们可以通过man指令来查看一下 open的基本信息
man 2 open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);pathname: 要打开或创建的目标文件;flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags;mode: 权限,创建文件时设置文件的起始权限(通常设置为 0666);参数:O_RDONLY: 只读打开O_WRONLY: 只写打开O_RDWR : 读,写打开这三个常量,必须指定一个且只能指定一个O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限O_APPEND: 追加写O_TRUNC: 清空原文件返回值:成功:新打开的文件描述符(descriptor)失败:-1
———— 我是一条知识分割线 ————
2.open接口中的flag标记位
(1)flag标记位介绍
下面是文档中对 flag的描述
- O_RDONLY - 只读
- O_WRONLY - 只写
- O_RDWR - 读写
- O_CREAT - 创建
- O_TRUNC - 清空文件
【注意】我们可以使用树划线 | (通道),将不同的flag选项串联起来
。
int fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);
(2)flag底层原理(仅作了解)
在C语言中,我们以一个整数(int)作为一个标记位。因为一个int由32个比特位构成,我们让1存在于32位比特位中的不同的位置,使用不同的比特位组合,实现对不同情况的标识。open中的flag底层就是使用这样的原理进行选项传递的。
下面是一个应用比特位作为标识,输出不同结果的代码示例
#include <stdio.h>
#include <unistd.h>
#include <string.h>// 每一个宏,对应的数值,只有一个比特位是1,彼此位置不重叠
#define ONE (1<<0)
#define TWO (1<<1)
#define THREE (1<<2)
#define FOUR (1<<3)void show(int flags)
{if(flags & ONE) printf("one\n");if(flags & TWO) printf("two\n");if(flags & THREE) printf("three\n");if(flags & FOUR) printf("four\n");
}int main()
{show(ONE);printf("-----------------------\n");show(TWO);printf("-----------------------\n");show(ONE | TWO);printf("-----------------------\n");show(ONE | TWO | THREE);printf("-----------------------\n");show(ONE | TWO | THREE | FOUR);printf("-----------------------\n");
}
代码运行起来之后,我们就可以根据不同的标记位,输出不同的内容
———— 我是一条知识分割线 ————
3.close
close - 系统级调用,关闭文件
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>#define FILE_NAME "log.txt" int main()
{// 以只读的方式打开int fd = open(FILE_NAME, O_RDONLY);if (fd < 0){perror("open");return 1;}close(fd);
}
———— 我是一条知识分割线 ————
4.write接口
通过 man查看 write的文档,
参数:fd - 想往哪个文件写,buf - 对应文件的缓冲区在哪里,count - 缓冲区对应的字节个数。
通过观察 write的接口,我们可以发现,有const void *
类型参数的存在,这也告诉了我们,无论其他编程语言传入的是什么类型的数据(文本、图片),在系统调用接口(操作系统)的层面,都可以将它们对应的二进制数据读取进去。
我们之前就提及过,在C语言的文件操作接口中,中以 w单纯代开文件,c会自动清空文件内部的数据,这是为什么呢?我们的系统调用接口也会自动帮我们做吗?实际上,我们的系统调用接口并不会在下一次打开时自动清空里面的数据,C语言的接口能完成是因为它做了相应的封装。
那我们要怎么样才能让系统调用接口在打开时就清空呢?这里我们就需要在open中添加一些 flag选项了。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>#define FILE_NAME "log.txt" int main()
{// 只写 + 新建//int fd = open(FILE_NAME, O_WRONLY | O_CREAT, 0666);// 只写 + 新建 + 清除原文件内容int fd = open(FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666);if (fd < 0){perror("open");return 1;}int cnt = 5;char outBuffer[64];while (cnt){sprintf(outBuffer, "%s:%d\n", "aaaa", cnt--);// 你以\0作为字符串的结尾,是C语言的规定,和我文件有什么关系呢?write(fd, outBuffer, strlen(outBuffer));}close(fd);
}
———— 我是一条知识分割线 ————
5.read接口
通过 man查看 read的文档,
参数:fd - 读哪个文件,buf - 对应文件的缓冲区在哪里,count - 缓冲区对应的字节个数。
read返回值,成功返回读到的字节数,0则代表读到了文件的结尾
。
假设我们的文件已经存在且其中保存的是字符串,虽然系统调用接口理论上可以读取任何类型的文件,但是我们还是需要根据实际情况,为read提供读取结束的依据,所以我们需要在下方增加一个字符串结束的判断。
int main()
{int fd = open(FILE_NAME, O_RDONLY);if (fd < 0){perror("open");return 1;}char buffer[1024];ssize_t num = read(fd, buffer, sizeof(buffer) - 1);if (num > 0) buffer[num] = 0; // 0, '\0', NULL -> 0 为读取提供结尾printf("%s", buffer);close(fd);
}
———— 我是一条知识分割线 ————
6.库函数接口和系统调用接口的关系
这里我们以C语言的库函数接口为例,我们使用库函数接口实际上底层都调用了对应的系统调用接口。
也就是说,库函数接口是系统调用接口的封装
。对应的,系统调用接口会将对应的返回值返回给库函数接口,使其能完成对应的功能
。(其他编程语言以此类推)
结语
🌹🌹 基础IO - 文件操作(使用系统接口实现) 的知识大概就讲到这里啦,博主后续会继续更新更多C++ 和 Linux 的相关知识,干货满满,如果觉得博主写的还不错的话,希望各位小伙伴不要吝啬手中的三连哦!你们的支持是博主坚持创作的动力!💪💪
相关文章:

【Linux初阶】基础IO - 文件操作(使用系统接口实现) | vim批量注释代码
🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux初阶】 ✒️✒️本篇内容:重新理解文件和文件操作,C语言实现的简单文件操作,文本初始权限,系统接口介…...
网络安全之信息收集
第一部分:被动信息收集 1、简介 在信息收集这块区域,我将其分为两部分:第一部分即被动信息收集,第二部分即主动信息收集。 对于标准的渗透测试人员来说,当明确目标做好规划之后首先应当进行的便是信息收…...

ModuleNotFoundError: No module named ‘_lzma‘
安装torchvision报错:ModuleNotFoundError: No module named ‘_lzma’ 参考文章:https://zhuanlan.zhihu.com/p/404162713 解决思路:用backports.lzma代替_lzma包 解决步骤:(ubuntu系统) 安装依赖sudo apt-get install liblzma-d…...
标点符号相关的英语单词
Comma - 逗号 Period - 句号 Question mark - 问号 Exclamation mark - 感叹号 Semicolon - 分号 Colon - 冒号 Quotation marks - 引号 Parentheses - 括号 Brackets - 方括号 Hyphen - 连字符 Dash - 破折号 Ellipsis - 省略号 Apostrophe - 省略符号 Slash - 斜杠 Backslash…...
MyBatis的部分知识点
一、resultMap的constructor配置方式 <resultMap id"" type""> <constructor> <!--主键--> <idArg column"id" javaType"_int"/> <!--其他列--> …...
PAT A1089 Insert or Merge
1089 Insert or Merge 分数 25 作者 CHEN, Yue 单位 浙江大学 According to Wikipedia: Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. Each iteration, insertion sort removes one element from the input…...

研发工程师玩转Kubernetes——创建一个测试容器
测试容器并不是什么都没有的容器,只是它没有我们期望的常驻进程。我们常用它来做一些测试。 举个例子,在《研发工程师玩转Kubernetes——自动扩缩容》中我们使用本地wrk进行了压力测试。如果我们希望进入容器手工调用wrk,该怎么做呢ÿ…...

FPGA - 7系列 FPGA内部结构之CLB -03- CLB相关原语以及应用
前言 本文节选UG474的第二章,进行整理翻译。CLB资源被FPGA综合工具自动有效地使用,不需要任何特殊的FPGA专用编码。一些HDL编码建议和技术可以帮助优化设计以获得最大效率。 设计检查清单 这些指南是为有效使用7系列CLB的设计建议提供的快速核对表。7…...

什么是日志关联
什么是日志关联 日志关联是一种分析来自不同源的日志数据以识别事件模式的技术。它用于更好地了解网络的活动,从而有效地保护网络免受漏洞和威胁。 日志关联是日志管理过程的关键部分。收集和存储日志后,集中式日志服务器将执行分析以检测特定事件。日…...

打家劫舍问题 Python题解
✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心&…...

【JavaSE】Java基础语法(十八):接口
文章目录 1. 接口的概述2. 接口的特点3. 接口的成员特点4. 类和接口的关系5. 抽象类和接口的关系 1. 接口的概述 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。Java中接口存在的两个意义 用来定义规范用来做功能的拓展 2. 接口的特点…...
SVD求解两组多维点之间的欧式变换矩阵,及halcon代码实现
之前研究了二维点的仿射变换,用解矩阵的方式求解了两组二维点之间的变换矩阵。 学习了下SVD,看到可以用SVD求解两组多维点之间的欧式变换矩阵,当然也是个最优化问题。 这里的变换只有平移和旋转,没有缩放。 一、先说结论&#…...

常用监控方案 Prometheus + Grafana 简单使用小结
文章目录 前言一、概念1.1 发展1.2 时序数据1.3 Metric 二、Prometheus2.1 架构2.2 配置2.3 查询语言PromQL2.4 Exporter 三、Grafana3.1 数据源3.2 权限3.3 面板可视化3.4 仪表盘 四、实战4.1 监控 Windows/Linux4.2 监控 JVM4.3 监控 MySQL4.4 监控 Springboot API 参考 前言…...
基于长短期神经网络LSTM的飞行轨迹跟踪预测,基于长短期神经网络LSTM的三维路径预测
目录 背影 摘要 LSTM的基本定义 LSTM实现的步骤 基于长短期神经网络LSTM的飞行轨迹跟踪 完整代码: https://download.csdn.net/download/abc991835105/87705046 效果图 结果分析 展望 参考论文 背影 路径追踪预测,对实现自动飞行驾驶拥有重要意义,长短期神经网络是一种改进…...

计算机组成原理-指令系统-指令格式及寻址方式
目录 一、指令的定义 1.1 扩展操作码指令格式 二、指令寻址方式 2.1 顺序寻址 2.2 跳跃寻址 三、 数据寻址 3.1 直接寻址 3.2 间接寻址 3.3 寄存器寻址 3.4 寄存器间接寻址 3.5 隐含寻址 3.6 立即寻址 3.7 偏移地址 3.7.1 基址寻址 3.7.2 变址寻址 3.7.3 相对寻址…...

【满分】【华为OD机试真题2023B卷 JAVAJS】经典屏保
华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 经典屏保 知识点循环迭代编程基础 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: DVD机在视频输出时,为了保护电视显像管,在待机状态会显示“屏保动画”,如下图所示,DVD Logo在屏幕内来回运动,碰到边缘会反弹…...

Apache 网页与安全优化
目录 一:Apache网页优化概述 1、概述 2、优化内容 3、gzip介绍 4、Apache的压缩模块 5、mod_ gzip 模块与mod_ deflate 模块 二: 网页压缩 1.检查是否安装 mod_deflate 模块 2. 如果没有安装mod_deflate 模块,重新编译安装 Apache 添…...
Unity的IFilterBuildAssemblies:深入解析与实用案例
Unity IFilterBuildAssemblies Unity IFilterBuildAssemblies是Unity引擎中的一个非常有用的功能,它可以让开发者在构建项目时自定义哪些程序集需要被包含在构建中,哪些程序集需要被排除在建之外。这个功能可以帮助开发者更好地控制项目的构建过程&…...

分片架构,Redis Cluster 分析
分片架构解决的问题 通过堆机器,提升读写性能,与存储性能 分片架构设计要点 分片规则 选择Cardinality大的作为分片键,尽可能保证数据分布均匀 常见分片键: 基于主键(业务型数据),基于时间…...
Linux-0.11 文件系统bitmap.c详解
Linux-0.11 文件系统bitmap.c详解 模块简介 该模块包含了两对函数,第一对是和i节点相关的free_inode()和new_inode()。第二对是和逻辑块相关的free_block()和new_block()。 函数详解 free_block void free_block(int dev, int block)该函数的作用是释放设备dev…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...