【Linux】解锁系统编程奥秘,高效文件IO的实战技巧

news2024/9/21 14:35:57

文件

  • 1. 知识铺垫
  • 2. C文件I/O
    • 2.1. C文件接口
    • 2.2 fopen()与重定向
    • 2.3. 当前路径
    • 2.4. stdin、stdout、stderr
  • 3. 系统文件I/O
    • 3.1. 前言
    • 3.2. open
      • 3.2.1. flags</h3>
      • 3.2.2. mode</h3>
      • 3.2.3. 返回值fd
    • 3.3. write</h2>
    • 3.4. read
    • 3.5. close</h2>
    • 3.6. lseek</h2>

1. 知识铺垫

  1. 文件=内容+属性。

文件属性、文件内容都是二进制数据,都需要占据磁盘的存储空间。

对文件的所有操作,本质上是要么对文件的内容做操作,要么对文件的属性做操作。

  1. 打开或修改文件,都是通过执行代码(如:文本编辑器)的方式来完成的。

文件打开、编辑、保存的流程:软件启动 -> 文件加载 -> 读取文件内容 -> 用户交互 -> 文件修改 -> 保存修改。

  1. 需要先打开文件,才能访问它 —> 将文件加载到内存中。

代码本身不能直接修改磁盘上的数据,CPU执行代码来修改这个文件时,实际上是在内存中进行的,因为CPU只能访问内存,所以磁盘的数据需要通过OS提供的文件系统调用接口和磁盘IO操作,来加载到内存中,然后CPU才能对这些数据进行处理。

  1. 谁在打开文件?进程。

打开文件之前,需要把访问这个文件的程序,编译形成的可执行程序,通过某种方式启动变成进程,等待cpu调度,执行完fopen后,文件才会打开。

  1. 一个进程可以打开多个文件吗?可以。

  2. 进程与文件的关系:结构体struct task_struct和结构体struct file之间的关系。

在一段时间内,系统存在多个进程,也可能同时存在多个被打开的文件,OS需要管理多个被打开的文件(先描述,再组织),所以内核中一定有描述被打开文件的结构体,并用其定义对象。

  1. 系统中是不是所有的文件都被打开了?不是,未打开的文件存放在磁盘中,被称为磁盘文件;打开的文件,存放在内存中,被称为内存文件。

2. C文件I/O

2.1. C文件接口

fopen、fclose

fread、fwrite; fgetc、fputc;fgets、fputs; fscanf、fprintf

fseek、ftell、rewind

feof、ferror

有关C文件接口,请看C语言博客中的文件内容,都有详细的介绍。

2.2 fopen()与重定向

  1. fopen以"w"方式打开:如果文件不存在,先会创建一个文件 / 如果文件存在,先会清空文件内容,然后再从头进行写入操作。
  • 相当于输出重定向(>)。
//文件以写的方式打开,如果文件不存在,新建文件 / 如果文件存在,先会清空文件内容,然后再从头进行写入操作
FILE *fp = fopen("example.txt", "w"); 
if (fp == NULL) {  //打开失败
    perror("Failed to open file"); 
    return 1;
}

fprintf(fp, "New content\n"); //向文件中进行写入
fclose(fp)  //关闭文件


2. fopen以"a"方式打开:本质也是写入,如果文件不存在,先会创建一个文件,然后进行写入 / 如果文件存在,会在文件原有内容的末尾处追加写入。

  • 相当于追加重定向(>>)。
//文件以追加的方式打开,如果文件不存在,先会创建一个文件,然后进行写入 / 如果文件存在,会在文件原有内容的末尾处追加写入
FILE *fp = fopen("example.txt", "a");
if (fp == NULL) {
    perror("Failed to open file");
    return 1;
}

fprintf(fp, "Additional content\n");
fclose(fp);


3. fopen以"r"方式打开:读取文件的内容,如果文件不存在,则读取失败,返回NULL 。

  • 相当于输入重定向(<)。
//以读的方式打开文件,读取文件的内容,如果文件不存在,则读取失败,返回NULL 
FILE *fp = fopen("example.txt", "r");
if (fp == NULL) {
    perror("Failed to open file");
    return 1;
}

char buffer[1024];
if(fgets(buffer, sizeof(buffer), fp) != NULL) //文件存在
    printf("%s", buffer);

2.3. 当前路径

  1. 当一个进程在启动时,它的当前工作目录(当前路径),通常是启动该进程时所在的路径。这个路径通常由父进程(Shell)决定,它会被保存到/proc/pid/cwd中。

fopen(“data.txt”,“w”),如果data.txt文件不存在,就会在当前路径下创建此文件。

int chdir(const char* path);

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
 
int main()
{ 
    printf("self id:%d\n", getpid());
    printf("更改前:当前工作目录\n");
     sleep(25);
 
     chdir("/root/tmp");
 
     printf("更改后:当前工作目录\n");
     sleep(25);                                                     

     FILE* fp = fopen("110.txt","w"); 
    //此处表示,如果不存在110.txt文件,就会在当前路径(cwd)下,创建此文件
     if(fp == NULL) return 1;
     fclose(fp);
     printf("文件创建成功\n");

     return 0;
}

2.4. stdin、stdout、stderr

一、标准输入流stdin

  1. 定义:标准输入是程序可以从中读取输入数据的位置,它默认指向键盘,但也可以被重定向为文件或者其他输入设备。

  2. 作用:允许用户通过键盘或者其他输入设备向用户提供数据,也可以从文件中读取数据。

  3. 文件描述符:在linux系统中,stdin文件描述符为0。

二、标准输出流stdout

  1. 定义:标准输出是程序用于发送其输出数据的位置,它默认指向终端屏幕,但也可以被重定向为文件或者其他输出设备。

  2. 作用:stdout用于显示程序的正常输出,包括结果、状态信息、其他非错误信息。

  3. 文件描述符:在linux系统中,stdout文件描述符为1。

  4. 缓冲:stdout通常是行缓冲的,意味着输出会先存储在缓冲区中,直到遇到换行符或者缓冲区满才会刷新到目的地。

三、标准错误输出流stderr

  1. 定义:标准错误是程序用于发送错误、异常信息的位置,它默认指向终端屏幕,但也可以被重定向为文件或者其他输出设备。

  2. 作用:用于输出错误信息,以便用户能够识别并解决问题。

  3. 文件描述符:在linux系统中,stderr文件描述符为2。

  4. 缓冲:stderr是非缓冲的,意味着错误信息会被立即发送到目的地,以便用户能够尽快的看到它。

💡注意:程序在启动时,默认会打开stdin、stdout、stderr流。即:可以直接使用它们,无需手动打开。

如:printf是向标准输出流stdout发送格式化数据,因为stdout在启动时已经被打开了,并关联到了输出设备(显示器),所以printf可以直接使用它们,而无需进行额外的打开操作。

问:为什么程序在启动时,默认会打开stdin、stdout、stderr流?

  • 原因:大部分的程序员,无论你用什么语言进行编程,需求就是让你的计算机去帮你加工和处理你的数据,就需要处理这三个问题(用户的数据从哪里来,计算完毕后怎么让用户看到对应的计算结果,计算过程中出错了怎么办),所以我们就需要stdin,给用户提供一个输入方式把数据输入进来,需要stdout,向一个特定的显示设备进行打印,方便给用户把计算结果显示出来,需要stderr,帮助用户把错误信息打印到特定设备中。大部分程序默认情况都要项你的程序输入数据,由你自己的程序计算完毕后,把结果进行返回,如果语言不提供,就需要程序员自己打开,太麻烦,所以程序默认给你打开这三个流(不用声明和定义,程序在启动时,就已经帮你初始化列表,直接使用就可以了)。

3. 系统文件I/O

3.1. 前言

一、访问文件不仅有C语言上的文件接口,OS必须提供对应的访问文件的系统调用接口。即:C标准库中的文件IO接口,底层一定封装了系统调用接口。

问:为什么C语言的文件接口,底层要封装对应的系统调用接口?

  1. 简化了操作,增强了安全性和稳定性。

文件进行读写操作,需要访问硬件,而OS作为软硬件资源的管理者,所以用户不能越过OS来直接访问硬件,OS会提供一组访问文件的系统调用接口 。

  1. 保证了语言的跨平台性、可移植性。

不同的操作系统有不同的系统调用接口和文件的操作机制。如果直接使用系统调用接口进行文件操作,那么编写的代码很难在不同OS之间移植。C语言标准库通过封装系统调用,为开发者提供了一套统一的文件操作接口,这样开发者就能够使用相同的代码,在不同OS上进行文件操作,提高了代码的跨平台性。

💡Tisp:如果让语言直接使用原生的系统接口,就注定了这个语言不具备跨平台性、可移植性。

二、在语言上,C、C++在访问文件时的接口和方式是不同的,在OS看来,底层原理是一样的,都是对底层系统调用接口的封装。

在C++中,主要是通过istream库中的fstream(ifstream、ofstream)类来实现,成员函数封装了对这些资源的操作,包括调用系统调用接口来实现底层的读写操作。

所有语言的文件操作不一样,但底层原理都是一样的,不随着意志而转移。上层就只要熟悉用法。

3.2. open

int open(const char* pathname,int flags);

  1. 功能:打开或创建一个文件。

  2. 参数pathname:要打开或者创建文件的路径名。

如果pathname为绝对路径时,当需要创建一个文件,会在pathname路径下创建它;

如果pathname为文件名时,当需要创建一个文件,会在当前路径(进程启动时所在的路径)下创建它。

  1. 返回值:打开成功,返回非负整数,即:文件描述符(用于后续文件操作);如果失败,返回-1,并设置errno以指示错误的原因。

fopen:"r"-> open:O_RDONLY
fopen:"w"-> open:O_WRONLY|O_CREAT|O_TRUNC
fopen:"a"-> open:O_WRONLY|O_CREAT|O_APPEND
  • 底层调用Open,传递不同的参数,在上层表现为fopen以r、w、a方式打开文件。即:不同的fopen风格,代表着open传递了不同的选项。

3.2.1. flags

  1. 参数flag:标记位,用于指定文件的打开模式(只读、只写、追加等)和其他选项(创建文件、截断文件等)。
//宏
#define O_RDONLY    0x00000000  // 只读模式  16进制
#define O_WRONLY    0x00000001  // 只写模式
#define O_RDWR      0x00000002  // 读写模式
//以上三个常量,必须且只能指定一个
#define O_CREAT     0x00000100  // 如果文件不存在,则创建文件
#define O_TRUNC     0x00000200  // 如果文件存在,则截断为零长度
#define O_APPEND    0x00000400  // 每次写入时追加到文件末尾
#define O_EXCL      0x00000800  // 如果文件已存在,则打开失败
#define O_NONBLOCK  0x00001000  // 非阻塞模式 
  1. flag参数是一个整数,每个比特位代表一个标记位。通过位操作(|、&),可以一次性向函数传递多个标记位。

在编程中,涉及需要向函数传递多个布尔选项(标记位)时,使用单个整数(int,32位),并通过位操作来设置和检查这些选项,这种方法被称为"位图",是一种非常高效和节省资源的方法。

  1. 使用宏定义,来表示各种标记位,每个宏定义只有一位为1(每个宏中为1的位是错开的),其余位全为0。在这个整数中为1的位,用来表示某个特定的选项是否被设置。多个宏通过位操作(|按位或)组合,一次性地向函数传递多个标记位。即:通过位图的方式,传递多个标记位。
#include<stdio.h> 

#define ONE 1
#define TWO (1 << 1)
#define THREE (1 << 2)
#define FOUR (1 << 3)
  
void print(int flag)
{
    if(flag & ONE) printf("1\n");
    if(flag & TWO) printf("2\n");
    if(flag & THREE) printf("3\n");
    if(flag & FOUR) printf("4\n");
}
 
 int main()
 {
     print(ONE);
     printf("-----------\n");
  
     print(ONE|TWO);
     printf("-----------\n");
  
     print(ONE|TWO|THREE);  //相当于是将两个宏中为1的位,进行了合并
     printf("-----------\n");

     print(ONE|TWO|THREE|FOUR);                                                      
     printf("-----------\n");
  
    return 0;
 }

3.2.2. mode

  1. 参数mode:可选参数,用于创建文件时设置文件权限。
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
   
int main()
{
    //以"w"方式打开文件,文件存在,就在当前路径下新建文件
    int fd = open("data.txt", O_WRONLY|O_CREAT|O_TRUNC);
    if(fd == -1)  //打开失败
    {
        perror("open");
        return 1;
    }
  
    return 0;                                              
}


现象:新建文件的权限乱码了。 -> 原因:新建文件,未设置文件权限。

#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
   
int main()
{
    //以"w"方式打开文件,文件存在,就在当前路径下新建文件
    int fd = open("data.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
    //int fd = open("data.txt", O_WRONLY|O_CREAT|O_TRUNC);
    
    if(fd == -1)  //打开失败
    {
        perror("open");
        return 1;
    }
  
    return 0;                                              
}

现象:文件权限并不是666。


现象:文件权限不为666。

原因:默认(最终)权限计算公式 = 起始权限 & (~umask值) , 本质是从起始权限中去掉在umask权限中出现的权限,如果在起始权限中某权限位不存在,但umask中该权限位存在,该权限位的结果为0,"去掉"不是删除。

mode_t umask(mode_t mask);

  • 功能:设置文件的权限掩码。

umask函数只会影响调用它的进程所创建的权限掩码,而不会对父进程或其他进程的权限掩码产生影响。

#include <sys/stat.h>
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
   
int main()
{
    umask(0);  //只改变此进程所创建的权限掩码
    
    //以"w"方式打开文件,文件存在,就在当前路径下新建文件
    int fd = open("data.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
    //int fd = open("data.txt", O_WRONLY|O_CREAT|O_TRUNC);
    
    if(fd == -1)  //打开失败
    {
        perror("open");
        return 1;
    }
  
    return 0;                                              
}

3.2.3. 返回值fd

一、FILE*与fd的关系

在底层上,fd是文件描述符,用于标识一个打开的文件,用于后续的文件操作。在语言上,FILE*是C语言标准I/O库定义的类型,用于表示文件流,用于后续的文件操作 —> 所以FILE结构体对象必定封装了fd。

  • FILE类型是C语言IO库定义的一个结构体类型,用于表示一个流,这个流可以关联到一个文件、内存区域或其他输出/输入资源。FILE结构体内部封装了与流操作相关的各种信息(如:文件描述符fd、文件读写位置、缓冲区、文件状态等)。

  • 流:抽象的概念,它提供了统一的方式来处理数据的输入、输出,无论这个数据是来自于文件、标准输入输出(键盘、显示器)或其他形式的输入输出资源。流的本质是数据传输。

#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h> 
  
int main()
{
    int fd1 = open("data1.txt", O_WRONLY|O_CREAT|O_TRUNC);
    int fd2 = open("data2.txt", O_WRONLY|O_CREAT|O_TRUNC);
    int fd3 = open("data3.txt", O_WRONLY|O_CREAT|O_TRUNC);
  
    printf("fd1:%d\nfd2:%d\nfd3:%d\n", fd1, fd2, fd3);
    printf("\n");                                          

    //FILEl类型是由C标准库封装的结构体
    printf("stdin:%d\n", stdin->_fileno);
    printf("stdout:%d\n", stdout->_fileno);
    printf("stderr:%d\n", stderr->_fileno);
  
    return 0;
}


二、C语言文件接口,不仅在接口上进行了封装,在类型上也进行了封装。

  1. 接口层面的封装

定义:将文件操作的具体实现隐藏起来,只向用户暴露了一组预定义的函数,用户就可以通过这组函数间接与文件系统进行交互。这种方式使得用户不需要了解文件系统背后复杂的机制。如:磁盘读写。

如:C语言标准库中的fopen、fclose、fread、fwrite等高级函数,底层封装了对应的系统调用接口open、close、read、write。用户通过这些函数可以打开文件、关闭文件、读取文件内容、写入文件内容,而不需要关心这些操作是如何在底层实现的。

  1. 类型层面的封装

定义:为文件操作定义了一个或多个特定的数据类型(如:FILE类型),用于表示文件状态和上下文。

如:FILE结构体封装了文件描述符、文件读写位置、缓冲区、文件状态等其他相关信息。

3.3. write

ssize_t write(int fd, const void *buf, size_t count);

  1. 功能:向打开的文件中写入数据。

  2. 参数:fd,表示写入数据的文件或设备; buf,指向要写入数据的缓冲区的指针; count,要写入的字节数。

如果buf为const char*类型,strlen(buff),不需要在后面+1,即:不需要把字符串结束标志\0写入进去。因为c语言字符串以\0结尾,\0不是字符串内容,而是作为字符串结束标记,与文件无关,若把\0写入,则会造成乱码。

  1. 返回值:如果成功,返回实际写入的字节数。如果出错,则返回-1,并设置errno以指示错误。

3.4. read

ssize_t read(int fd, void *buf, size_t count);

  1. 功能:从打开的文件中读取数据。

  2. 参数:fd,表示要读取数据的文件或设备; buf,指向读取数据的缓冲区的指针; count,要读取的最大字节数。

  3. 返回值:如果成功,返回实际读取的字节数。如果出错,则返回-1,并设置errno以指示错误。如果到达文件末尾(EOF),则返回0。

3.5. close

int close(int fd);

  1. 功能:关闭一个打开的文件描述符。

  2. 返回值:如果成功,返回0。如果失败,返回-1,并设置errno以指示错误。

3.6. lseek

off_t lseek(int fd, off_t offset, int whence);

  1. 功能:移动文件指针的位置。

  2. 参数offset:是从whence指定位置开始的偏移量,以字节为单位(在左边,则为负值、在右边,则为正值)。

参数whence:指定偏移量的参考点。

  1. 返回值:如果成功,返回新的文件偏移量(相对于文件开头的字节数)。如果出错,则返回-1,并设置errno以指示错误。

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

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

相关文章

快速响应:提升前端页面加载速度技巧的必知策略方案

在本文中&#xff0c;我们将深入探讨导致页面加载缓慢的常见原因&#xff0c;并分享一系列切实可行的优化策略&#xff0c;无论你是刚入门的新手&#xff0c;还是经验丰富的开发者&#xff0c;这些技巧都将帮助你提升网页性能&#xff0c;让你的用户体验畅快无阻。 相信作为前端…

【JavaEE精炼宝库】HTTP | HTTPS 协议详解

文章目录 一、HTTP 简介二、HTTP 协议格式&#xff1a;2.1 抓包工具的使用&#xff1a;2.2 HTTP 请求报文格式&#xff1a;2.3 HTTP 响应报文格式&#xff1a;2.4 HTTP 协议格式总结&#xff1a; 三、HTTP 请求详解&#xff1a;3.1 刨析 URL&#xff1a;3.2 方法(method)&#…

极度精简 Winows11 系统镜像!Tiny11 2311下载 - 支持苹果 M 芯片 Mac 安装 (ARM 精简版)!

最新推出的 Tiny11 是一款极端精简版 Windows 11 系统镜像&#xff0c;针对苹果 M 芯片 Mac 用户&#xff08;ARM 架构&#xff09;提供良好支持。Tiny11 内置了众多优化特性&#xff0c;如更小的安装体积和更快的启动速度&#xff0c;特别适合有特殊需求或老机型的用户。用户可…

centos 安装VNC,实现远程连接

centos 安装VNC&#xff0c;实现远程连接 VNC(Virtual Network Computing)是一种远程控制软件&#xff0c;可以实现通过网络远程连接计算机的图形界面。 服务器安装VNC服务 yum install -y tigervnc-server*启动VNC服务&#xff0c;过程中需要输入连接密码 vncserver :1查看…

2024华为杯研究生数学建模C题【数据驱动下磁性元件的磁芯损耗建模】思路详解

问题一 励磁波形分类 励磁波形作为影响磁芯性能的核心要素之一&#xff0c;其形态深刻影响着磁芯的损耗特性。励磁波形的独特形状直接塑造了磁芯内部磁通的动态行为&#xff0c;不同的波形轮廓影响了磁通密度随时间的变化速率&#xff0c;导致其损耗特性呈现出显著差异。因此&…

ESP32本地大模型对话机器人制作教程

整体架构 在本地电脑部署好Ollama服务&#xff0c;安装qwen大模型和llama3.1大模型。 ESP32接入局域网&#xff0c;用户通过串口给esp32发送问题&#xff0c;esp32打包json后向ollama服务发送请求&#xff0c;ollama返回响应&#xff0c;esp32解析结果并通过串口打印出来。 …

MavenMyBatis

Maven&MyBatis 目标 能够使用Maven进行项目的管理能够完成Mybatis代理方式查询数据能够理解Mybatis核心配置文件的配置 1&#xff0c;Maven Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供了一套标准化的项目结构 提供了一套标准…

24最新Stable Diffusion 本地部署教程!

前言 1.前言&#xff1a; 最近看Stable Diffusion开源了&#xff0c;据说比Disco Diffusion更快&#xff0c;于是从git上拉取了项目尝试本地部署了&#xff0c;记录分享一下过程~ 这里是官网介绍&#xff1a;https://stability.ai/blog/stable-diffusion-public-release 嫌弃…

解决【WVP服务+ZLMediaKit媒体服务】加入海康摄像头后,能发现设备,播放/点播失败,提示推流超时!

环境介绍 每人搭建的环境不一样&#xff0c;情况不一样&#xff0c;但是原因都是下面几种&#xff1a; wvp配置不当网络端口未放开网络不通 我搭建的环境&#xff1a; WVP服务&#xff1a;windows下&#xff0c;用idea运行的源码 ZLM服务&#xff1a;虚拟机里 问题描述 1.…

计算机视觉的应用34-基于CV领域的人脸关键点特征智能提取的技术方法

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用34-基于CV领域的人脸关键点特征智能提取的技术方法。本文主要探讨计算机视觉领域中人脸关键点特征智能提取的技术方法。详细介绍了基于卷积神经网络模型进行人脸关键点提取的过程&#xff0c;包括使…

基于springboot学生健康管理系统的设计与实现

文未可获取一份本项目的java源码和数据库参考。 进入21世纪以来&#xff0c;随着经济水平的高速发展&#xff0c;人们的生活质量有了很大提升&#xff0c;物质和精神生活得到了极大满足。但人们的健康水平却情况堪忧。据不完全统计&#xff0c;全国近七层人口处于亚健康状态&a…

数据结构(Day18)

一、周学习内容 1、9.18 数据结构&#xff08;Day15&#xff09;-CSDN博客 2、9.19 数据结构&#xff08;Day16&#xff09;-CSDN博客 3、9.20 链表 目的 插入删除不需要移动任何节点&#xff08;元素&#xff09;。 不需要预估存储空间大小&#xff0c;长度动态增长或减小。…

Linux文件IO(六)-多次打开同一个文件

大家看到这个小节标题可能会有疑问&#xff0c;同一个文件还能被多次打开&#xff1f;事实确实如此&#xff0c;同一个文件可以被多次打开&#xff0c;譬如在一个进程中多次打开同一个文件、在多个不同的进程中打开同一个文件&#xff0c;那么这些操作都是被允许的。本小节就来…

PyRosetta打分函数介绍

在 PyRosetta 中,蛋白质结构的能量是通过打分函数(ScoreFunction)来评估的,这些打分函数基于 Rosetta 的能量方程。Rosetta 的能量函数是一种加权的分项能量表达式,包括不同的能量项来描述蛋白质的构象、相互作用和能量。核心能量函数的形式如下: 在 PyRosetta 中,打分函…

神经网络推理加速入门——一个例子看懂流水

之前的两篇文章介绍了流水这一技术&#xff0c;它用来进行程序的性能加速&#xff0c;本篇通过一个生活中的小例子&#xff0c;让大家更直观的了解什么是流水。 举个例子 早晨从起床到上班出门&#xff0c;我们一般会做以下几件事&#xff1a;刷牙、烧水、喝水、出门。 如果…

应届生必看 | 毕业第一份工作干销售好不好?

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330&scene21#wechat_redirect 《网安面试指南》…

都2024年了,看谁还不会用AI作图?这个全方位的系统教程真的别错过了!

大家好&#xff0c;我是画画的小强 如果给我们现在所处的时代一个标签&#xff0c;相信很多人都会选择人工智能。 其实&#xff0c;关于 AI 的讨论已经不局限在科学家和算法工程师之间&#xff0c;更多的是在各行各业的从业者之间&#xff0c;甚至也出现在了高考试卷中。 以…

多旋翼无人机维修、组装、调试技术详解

多旋翼无人机作为现代航拍、农业植保、物流运输等领域的重要工具&#xff0c;其性能的稳定性和操作的便捷性对于任务的完成至关重要。因此&#xff0c;掌握多旋翼无人机的维修、组装与调试技术&#xff0c;对于无人机操作员及维修人员来说至关重要。本文将详细介绍这三个方面的…

96 kHz、24bit 立体声音频ADC芯片GC5358描述

概述&#xff1a; GC5358 是一款高性能、宽采样率、立体声音频模数转换器。其采样率范围是8KHz~96KHz&#xff0c;非常适合从消费级到专业级的音频应用系统。单端模拟输入不需要外围器件。GC5358 音频有两种数据格式&#xff1a;MSB对齐和 I2S 格式&#xff0c;和各种如 DTV、D…

将Java程序打包成EXE程序

Java制作可执行jar 方式一&#xff1a;mainClass与lib分离 1&#xff09;将Java程序依赖的所有jar都拷贝在lib目录下&#xff0c;并添加到classpath中 2&#xff09;运行时指定MainClass pom.xml 这个pom.xml生成的jar可双击直接运行&#xff0c;但是因为没有将其依赖的jar…