linux学习之详解文件

news2024/11/23 20:06:47

目录

1.先认识文件

2.c语言中常用文件接口

fopen(打开文件)

3.系统接口操作文件

open

 write

文件的返回值以及打开文件的本质

理解struct_file内核对象

了解文件描述符(fd)分配规则

重定向

dup接口

标准错误流

文件缓冲区

举例

总结:

简单接口的封装


1.先认识文件

首先了解文件之前,我们必须清楚的了解文件的本质:

1.文件=内容+属性,对文件的操作,就是对内容或者属性操作。

2.其实内容属于数据,属性也是属于数据的。存储文件,既要存储文件内容们也要存放文件属性。

3.我们(进程)在访问文件时,首先要打开这个文件,打开前他是存储在磁盘中的,打开后需要把文件加载都内存当中。

4.一个进程可打开多个文件,多个文件都会被加载到内存当中。

而打开文件这些工作也是操作系统干的事,因此操作系统要管理这些文件,就需要先描述在组织:

首先描述出文件的各个属性及内容,再将它组织成一个结构体,即文件的内核数据结构,通过链表的方式链接这些结构体,之后对文件实现增删查改就是操作链表。

因此打开一个文件,操作系统会在内核中形成一个被打开的文件对象来描述该文件。

5.因此一个打开的文件,与一个未被打开的文件两者此时是不一样的,没打开的文件就在磁盘里,打开的文件需要被加载到内存当中并形成对应的文件对象结构表示该文件,并用来管理该文件。

2.c语言中常用文件接口

fopen(打开文件)

 先看第一个fopen参数第一个是文件路径,第二个是文件句柄(文件指针)。

  1 #include<stdio.h>
  2 
  3 int main()
  4 {
  5   FILE *p=fopen("log.txt","w");//以下的方式在当前路径下打开一个log.txt文件
  6   if(p==NULL)
  7   {
  8     perror("fopen");//打开失败,打印错误信息
  9     return 1;
 10   }
 11   const char*str="hello linux\n";
 12   int cnt=10;
 13   while(cnt--)
 14   {
 15     fputs(str,p);//像文件里面写入十行字符串                                                                                                           
 16   }
 17   fclose(p);//关闭文件
 18   return 0;
 19 }

基本了解一下常用接口。

对于文件的打开方式,我们再看一下:

 r:以读的方式打开文件,只能读。

r+:支持读文件,也支持写入,写入在文件末尾写。

对于w,以写方式打开的时候时截断的,即每次会先清空之前的内容,文件未创建会创建。即覆盖式打开文件。

而我们的重定向>文件,就是以w方式打开文件,当我们需要清空文件内容时,就是用重定向方式打开文件。

w+:与w方式一样,不过增加了读。

a:也是文件写入,只不过写入是从文件结尾写入,即追加写入。

我们也可以利用追加重定向的符号进行写入 >>文件。

a+:与a效果一样,增加了读。

3.系统接口操作文件

上述c语言操作文件的接口,在底层其实都是操作系统打开文件的指令被封装成c接口。

因此我们来尝试用系统接口操作文件:

open

首先对于open的返回值,如果打开成功,它会返回一个文件描述符(简称fd),失败会返回-1和错误信息。

第一个参数文件名,第二个参数flags,表示打开文件的标记位。

对于标志位,可能会右很多参数,对于linux下,传参方式是这样的:

这里的一个标志位,是可以传多个标志位的,

如果文件存在:

就是这里的O_RDONLY(以读方式打开),O_WRONLIY(以写的方式打开),O_RDWR(以读写的方式打开).

若果文件不存在:

标志位是下面这些,O_CREAT(创建文件),O_DIRCTORY(),O_NOCTTY().......

 

 实际上这些标志位就是一个宏(整数),这里的每一个宏都是唯一的。

通过宏传参,我们可以实现对于满足宏条件的语句都可以执行,因此可以实现多个一起传入参数,且实现其中的多个功能。

如下一个简单的程序:

 #include<stdio.h>
   #include<unistd.h>
   #include<sys/types.h>
   #include<sys/stat.h>
   #include <fcntl.h>
   int main()
   {
    int fd=open("log.txt",O_WRONLY|O_CREAT);//一些的方式打开,不存在就创建
     if(fd<0)
    {
     perror("open failed");
      return 1;
    }                                                                                                                                                   
    close(fd);
    return 0;
  }                                             

因为我们并没有提供文件属性设置的指令,因此这里会是乱码的。

 因此我们可以看到对于函数open第二个是有三个参数,第三个参数就代表设置文件的权限:

这里的权限就是对应的8进制数:

当我们在打开时这样写:

 int fd=open("log.txt",O_WRONLY|O_CREAT,0666);

 但是这里的权限表示的是664,主要原因创建权限时,系统中会有权限掩码帮我们过滤我们设置的权限,可利用umask函数修改掩码。

//再open前加入
umask(0)//权限掩码设置为0

 之后就可以看见都是6660了。

 我们在一般使用的时候,就使用系统的umask。

 write

打开晚间后,那么我们就可以操作文件了,最常用的就是文件写入了

向一个指定的文件描述符写入。

参数也不难看出是 fd文件描述符  buf写的字符串 count个数

char*str="hello,linux!";
write(fd,str,strlen(str));

其次,我们再写入时,如果之前有内容,并不会清空,而是从头开始覆盖式的写入,因此我们再写入之前如果想写入新的数据,需要添加新的标志位-(O_TRUNC),截断文件为0。

int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);

若我们想要在文件结尾处写入,则式将这里的O_TRUNC换成0_QPPEND,追加写入。

int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);

看到这里,我们就会发现如何去将系统库函数封装成c语言的接口。

文件的返回值以及打开文件的本质

    当我们在代码创建了多个文件,将他们的文件描述符一一打印出来的时候,我们会发现他是从3开始,逐渐增加,且是连续的,逐渐增加我们明白,因为文件标识符不可能一样,通过++的方式确保每一个文件的文件描述符是唯一的,但是为什么是从3开始的呢?

首先和管理进程一样,在管理文件是操作系统要为每一个打开的文件创建对应的内核结构struct_fIle)管理该文件,结构体里面存放的就是描述文件的各种属性。

 为了区分好文件结构体与进程的结构体,确定那个进程有哪些文件,文件被打开时,系统创建对应的文件结构体struct_file,同时会在该进程有一个数组里存放文件的地址,并将此时的下标返回给上级,通过此方式,哪些进程的哪些文件就可以被知道,且两者更好能区分。且这个下标就是我们这里的fd,进程标识符。

知道了这些,那么为什么不是从0开始的呢?前三个数组下标呢?

实际上,这个我们之前在学c语言的时候就会知道文件在打开时,会默认打开三个文件流:

标准输入流 stdin    键盘                 0

标准输出流 stdout  显示器             1

标准错误流 stderr   显示器           2

这其实也就是前三个文件描述符。

我们知道c语言里FILE*来表示文件的开的返回值,那么是什么?

FILE*    其实是一个结构体,由c语言标准库提供,作为一个结构体,他其中必定封装有fd(文件描述符),其实对应的返回值与函数c语言库中都进行了封装。

int main()  
   {
   
   printf("stdin->fd",stdin->_fileno);
   printf("stdout->fd",stdout->_fileno);
   printf("stderrer->fd",stderr->_fileno);
  
   FILE*p=fopen("log.txt","w");
  
   printf("FILE->fd",p->_fileno);
   fclose(p);
  }         

 了解了以上,那么为什么文件被打开时要默认打开这三个文件流?

默认打开这三个就是为了让用户直接读写数据,直接用函数接口操作文件。

其次在每一个文件架构体重都会有两个函数指针,red,wirte。对于键盘,显示器,这样的书输入输出设备,他们就会有对应的读写方法。

理解struct_file内核对象

我们可以大概猜到struct_file与进程的内个对象类似,里面存放了各种关于文件的信息(属性,内容), 对于结构体的创建,在内存当中被如何创建我们都可以类比进程。对于文件,我们想要读数据时,需要将文件加载到内存当中,其次写数据也是需要我们将数据先加载到内存当中,而文件数据加载到内存当中并不是直接加载到内存当中,而是先加载到文件缓冲区当中,那我们我们读写数据的本质就是在缓冲区里来回拷贝。

了解文件描述符(fd)分配规则

我们已经知道了fd本质就是文件结构体指针数组的下标,那么在实际使用时如何分配的呢?

我们继续了解一下read接口,向指定的文件标识符中读取数据。

既然默认打开三个流文件,我们来试试这

先看看输入流文件,我们知道它对应的文件标识符位0,我们直接通过read读取输入缓冲区的字符:

int main()
   {
     char buffer[100];
    //因为文件在打开时默认打开了三个流,因此我们可以直接做读写操作
    read(0,buffer,100);//给输入流读取buffer数组大小的字符串,及我们输入的字符串保存在buffer
    printf("%s",buffer);
    return 0;                                                                                                                                           
  }                                                            

通过打印侧面证明了我们可以访问输入这个文件。不用通过scanf来读字符。

int main()
   {
     char buffer[100];
    read(0,buffer,100);//给输入流读取字符串,并保存在buffer
    write(1,buffer,strlen(buffer));//给输出流写入字符串buffer                                                                                           
    return 0;                                                  
  }          

所以fd的分配规则就是默认先打开这三个文件流,0,1,2.

且在分配的时候,从下开始找最小的,没有被使用的位置,再分配给打开的指定的文件。

当然我们也可以打开文件,再关闭这三个默认的文件流,例如close(1),那么我们想要打印出的数据就打印不出了。

重定向

基本了解以上内容之后,我们看看这个代码,是为什么呢?

 int main()
 {
    close(1);//关闭输出流
    int fd=open(FILENAME,O_CREAT| O_WRONLY | O_TRUNC,0666);
   if(fd<0) 
    {                                                                                          
      perror("open failed");                                   
      return 1;                                 
    }                
    printf("%d\n",fd);                                   
    printf("stdout->%d\n",stdout->_fileno);                                                                                                             
    fflush(stdout);      
    close(fd);     
  }

首首先我们关闭了输出流,运行程序之后,没有输出信息,没问题,关闭之后我们又打开了一个文件,该文件的输入输出错误流是打开的,此时我们进行打印之后,就刷新缓冲区,打印的信息,没出来,正常,但是当我们打开文件后,却发现写入显示器的内容到了文件里面,而这就是输出重定向。

那么为什么会是这样呢?

总结的来说就是,刚开始关闭了输入流,这里的结构体文件指针下标1对应没有指向(原本只想显示器),此时又打开了一个文件,(此时操作系统要看当前文件与此时打开的文件的关联,即打开我们.c文件时,下标0,2对应的指针又指向,1被关闭了,此时再次打开一个文件,则该文件地址要放到结构体指针数组,且要从最小的没被占用开始,所以该文件指向了下标1,即我们新打开的这个文件的描述符变成了1),因此打印进文件当中了。

因此,文件结构体数组下标没变,我们改变下标对应的内容,就可以实现重定向。

上述就是输入流:stdout->显示器转变stdout->文件

例如我们这里可以重新让他输入,而且这里我们使用fprintf接口,与printf差不多,只是多了我们需要的接口。

printf的stdout默认是向显示器里输出,利用fprintf我们可以决定向哪个地方输出,

将上述的代码的两个打印变成这样写:

 fprintf(stdout,"%d\n",fd);                                   
 fprintf(stdout,"stdout->%d\n",stdout->_fileno); 

取消关闭输出流,在运行就可以看到会向显示器打入:

此时新打开的文件标识符位3,默认stdout的是1 。向显示器打印。此时数据1位置指向的是显示器。

再关闭,在运行,此时1位置对应的指向是文件,我们就发现他就是相对应文件里打印数据。

根据上述结论可以验证我们的想法。

打开文件的文件标识符为1,stdout也为1/

但是对于这里我们是通过刷新缓冲区测i可以,去掉刷新缓冲区,文件里也没写入,这与我们的文件缓冲区有关。

通过上述方式,我们很容易实现输出输入重定向。

我们可以从文件读数据,从键盘读数据,我们也可以向显示器打印数据,也可以向文件打印数据。

上层fd不变,指向的内容在改变。

dup接口

我们可以看到关于dup函数的功能,它会改变旧文件的文件描述符,即改变文件指针的指向。且dup会默认关闭旧的文件流(这里就是显示器)。

通过该种方式,我们也可以实现文件描述符的重定向,其中在文件结构体内部还定义了一个引用计数的fcount,有几个文件指向结构体数组的某个位置,对应fcount就为几,当fcount为0时就会释放掉。当然我们也可以自己再重定向之前关闭这个文件流,不关闭系统也会帮我们重新指向。

int main()
   {
    int fd=open(FILENAME,O_CREAT| O_WRONLY | O_TRUNC,0666);
    if(fd<0)
    {
      perror("open failed");
      return 1;
    }
    //利用dup接口实现重定向,我们这里替换下标为1的输出流文件
    dup2(fd,1);//把原本文件的fd变成1,此时下标为1的地址指向的是文件
    fprintf(stdout,"file->%d",fd);
    fprintf(stdout,"stdout->%d",stdout->_fileno);   
      return 0;
}        

了解到重定向的本质,我们就可以通过命令行参数,来自己实现输入重定向符号>,追加重定向>>。

其次进程替换与重定向两者之间是没有影响的。程序替换只是地址映射发上了变换,并没有创建新的进程。,因此不影响重定向。

标准错误流

标准输入输出存在,我们很清楚,每次都要先打开这两个流我们也能理解,那么标准错误流,为什么每一次也要打开,错误流是干什么用的。

首先除了重定向输入流,输出流,我们也是可以重定向错误六,例如我们可以将错误流重定向到输出流。

1>log.txt   2>&1,通过连续的重定向我们可以实现,将多个文件的内容指向输出流1。通过该种方式我们就可以看到错误信息,方便我们排查。

文件缓冲区

当一个文件被访问时,无论读写都需要我们将它加载到文件缓冲区当中。即我们读写的数据都先事先拷贝到一个buffer里,再通过write接口将内容写到文件当中,且一个文件的缓冲刷新机制是全缓冲的。

那么什么是缓冲区,如何去理解缓冲区?

对于缓冲区,就是一部份内存,我们将数据拷贝其中,无论是C语言还是c++,我们都里理解为是malloc开辟出来的一块空间。

那为什么要有缓冲区呢?

缓冲区的作用是为了提高使用者的效率,我们不再把数据直接交给对方,而是先直接交给缓冲区,对于用户我们就直接完成了工作,缓冲区之后的工作让操作系统来搞。

因为有缓冲区,我们可以积累一部分派发数据,拥有不同的派发效果。当然积累数据的不同程度,缓冲区有不同的派发策略-----(无缓冲,行缓冲,全缓冲....)。其次也有特殊情况下的策略-------如强制刷新,进程退出(强制刷新)。

用过这种方式也提高了发送的效率。

举例

下来我们分析一下这段代码、

    #include<stdio.h>
  2 #include<unistd.h>
  3 #include<string.h>
  4 int main()
  5 {
  6 
  7   fprintf(stdout,"hello linux1\n");
  8   fputs("hello linux2\n",stdout);
  9   printf("hello linux3\n");
 10   const char *str="hello write\n";
 11   write(1,str,strlen(str));
 12 
 13   fork();//注意在该位置创建的子进程                                    
 14   return 0;
 15 }

运行之后的输出,和重定向到log.txt的输出,为什么是这样子的?

对于write接口的数据先写到文件当中,其次是c的接口的写入,且写了两份?

首先第一次直接输出到显示器上的,没问题,创建的子进程的位置也是在最后,与他无关。

输入正常。

第二次重定向到文件之后,再打开里面的内容为什么是这样子的呢?

这里的fork是如何执行的呢?

首先我们的缓冲区刷新是行刷新,因为我们在每一句话后面都带\n了,即在fork之前,缓冲区都是刷新了的。

但是当我们通过输出重定向到log.txt,此时我们依然知道这里的文件结构一直真的数组下标1里面不是指向显示器的,而是指向log.txt的文件当中了,此时的缓冲区刷新由行刷新变成了全缓冲,

全缓冲意味着缓冲区变大,更加意味着,我们写入的简单数据不足以把缓冲区写满,因此fork在执行的时候,此时数据还在缓冲区中。

目前我们这里所说的缓冲区与操作系统无关,而是c语言的封装,因为我们的接口printf,fprintf底层都是write的封装,但是这里取有缓冲区的概念,系统接口本身是没有这的。因此这里的缓冲区就是与c语言本身自己有关。

而我们这里的c/c++提供的缓冲区所保存的数据,属不属于进程的数据呢?实际上该缓冲区的数据属于进程本身的数据。

但是对于文件里面的数据,他与进程是什么关系呢,文件里的数据与进程的此时来说因该是没有啥关系的,即文件里的数据不属于进程的数据,当我们把缓冲区的数据交给到操作系统时,此时的数据就与进程没什么关系,而是属于文件。

因此当进程退出的时候,一般要刷新缓冲区,而刷新缓冲区就是要把这里的数据写到文件当中。

而父子进程,数据共享,当一方写入到文件中时(对于这里来说就是任意一个进程退出,强制刷新缓冲区,像文件里写入),两方都发生写时拷贝,因此我们的数据写入了两份。

但是对于系统调用接口,他与缓冲区没有任何关系,他是直接写入的,因此系统接口只写一次。

而且系统接口先进行写入,之后缓冲区刷新,再写入缓冲区的数据。因此我们看到先写的write的内容,之后是两份拷贝。

总结:

对于系统的文件读写接口,直接接像文件写入的,但是c/c++等语言不仅对读写接口进行了封装,还提供了缓冲区,使得读写数据对缓冲区操作,缓冲区的数据属于进程,但当刷新写入文件当中时,此时的数据不属于进程,而属于操作系统(文件)。

对于c语言,我们的缓冲区就封装在FILE中(由许多指针构成的一块空间)。

简单接口的封装

.h文件

pragma once
#define MAXSIZE 1024
//三种刷新策略
#define Flushline 1
#define Flushall  2
#define Flushnone 3

typedef struct _myFILE
{
  int fileno;//文件标识符
  char buffer[MAXSIZE];//缓冲区
  int end;//占用空间
  int flag;//标志
}myFILE;

myFILE* my_fopen(const char *path,const char* mode);
int my_fclose(myFILE*fp);
int my_fputs(const char*s,int num,myFILE* stream);
int my_fflush(myFILE*stream);

.c文件

#include"mystdio.h"
#include<string.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<errno.h>
#include<unistd.h>
#define permison 0666
//简单的封装io接口


myFILE* my_fopen(const char *path,const char* mode)
{
  int fd=0;//文件标识符
  int flag=0;//标志位,模式
  //打开文件模式
  if(strcmp(mode,"r")==0)
  {
     //以读的方式打开
    flag |=O_RDONLY;//只读

  }else if(strcmp(mode,"w")==0)
  {
     //以写的方式打开
    flag|=(O_WRONLY | O_CREAT |O_TRUNC);
    
  }else if(strcmp(mode,"a")==0)
  {
     //以追加的方式
   flag|=(O_WRONLY|O_CREAT|O_APPEND);
  }
  //打开文件
  if(flag & O_CREAT)
  {
    //若文件不存在,创建文件
    fd=open(path,flag,permison);

  }else{
    fd=open(path,flag);
  }
  if(fd<0)
  {
    errno=2;
    return NULL;
  }


  //打开成功,返回
  myFILE*p=(myFILE*)malloc(sizeof(myFILE));
  if(!p)
  {
    //申请失败
    errno=3;
    return NULL;
  } 
  p->flag=Flushline;
  p->fileno=fd;
  p->end=0;
  return p;
}
int my_fclose(myFILE*stream)
{
 my_fflush(stream);
 //把数据刷新到内核当中
 //fsync(stream->fileno);
 return close(stream->fileno);
}
int my_fputs(const char*s,int num,myFILE* stream)
{
   //把字符写到缓冲区当中
   memcpy(stream->buffer+stream->end,s,num);//从end位置处写入
   stream->end+=num;
   
   //判断\n ,遇到就要刷新
   if(stream->flag & Flushline&& stream->end>0 &&(stream->buffer[num-1]=='\n'))
   {
     my_fflush(stream); //行刷新
   }
   return stream->end;
   
}
int my_fflush(myFILE*stream)
{   
  //若缓冲区为空,就直接关闭
  if(stream->end==0)
  {
    close(stream->fileno);
  }
    //刷新,写到文件中
  write(stream->fileno,stream->buffer,stream->end); 
  stream->end=0;
  return 0; 
}

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

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

相关文章

【项目问题解决】IDEA2020.3 使用 lombok 插件 java: 找不到符号 符号: 方法 builder()

目录 lombok找不到符号问题修改 1.问题描述2.问题原因3.解决思路4.解决方案5.总结6.参考 文章所属专区 项目问题解决 1.问题描述 IDEA2020.3 使用 lombok 插件 java: 找不到符号 符号: 方法 builder()&#xff0c;无法使用lombok下应有的注解&#xff0c;一度怀疑是版本问题 …

二十一章(网络通信)

计算机网络实现了多台计算机间的互联&#xff0c;使得它们彼此之间能够进行数据交流。网络应用程序就是在已连接的不同计算机上运行的程序&#xff0c;这些程序借助于网络协议&#xff0c;相互之间可以交换数据。编写网络应用程序前&#xff0c;首先必须明确所要使用的网络协议…

hive映射es表任务失败,无错误日志一直报Task Transitioned from NEW to SCHEDULED

一、背景 要利用gpt产生的存放在es种的日志表做统计分析&#xff0c;通过hive建es的映射表&#xff0c;将es的数据拉到hive里面。 在最初的时候同事写的是全量拉取&#xff0c;某一天突然任务报错&#xff0c;但是没有错误日志一直报&#xff1a;Task Transitioned from NEW t…

MySQL数据库从小白到入门(二)

多表关系&#xff1a; 项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构。由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xff0c;基本上分为三种。 外键&#xff1a; 创…

JavaSE基础50题:11. 输出一个整数的每一位

概述 输出一个整数的每一位。 如&#xff1a;1234的每一位是4&#xff0c;3&#xff0c;2&#xff0c;1 。 个位&#xff1a;1234 % 10 4 十位&#xff1a;1234 / 10 123 123 % 10 3 百位&#xff1a;123 / 10 12 12 % 10 2 千位&#xff1a; 12 / 10 1 代码 ublic sta…

LinuxBasicsForHackers笔记 -- 管理用户环境变量

查看和修改环境变量 env – 您可以通过从任何目录在终端中输入 env 来查看所有默认环境变量。环境变量的名称始终为大写&#xff0c;如 HOME、PATH、SHELL 等。 查看所有环境变量 set – 查看所有环境变量&#xff0c;包括 shell 变量、局部变量和 shell 函数&#xff08;例…

java学习part40collections工具类

162-集合框架-Collections工具类的使用_哔哩哔哩_bilibili 1.collections工具类 感觉类似c的algorithm包&#xff0c;提供了很多集合的操作方法 2.排序 3.查找 4.复制替换 5.添加&#xff0c;同步

蓝桥杯day03——Bigram 分词

1.题目 给出第一个词 first 和第二个词 second&#xff0c;考虑在某些文本 text 中可能以 "first second third" 形式出现的情况&#xff0c;其中 second 紧随 first 出现&#xff0c;third 紧随 second 出现。 对于每种这样的情况&#xff0c;将第三个词 "th…

计数排序(C语言实现)

文章目录 算法思想操作步骤计数排序的特性总结代码实现 算法思想 计数排序是一种非比较排序&#xff0c;又称为鸽巢原理&#xff0c;是对哈希直接定址法的变形应用。 操作步骤 统计相同元素出现次数&#xff1b;根据统计的结果将序列回收到原来的序列中。 计数排序的特性总…

数据库:JDBC编程

专栏目录 MySQL基本操作-CSDN博客 MySQL基本操作-CSDN博客 数据库的增删查改&#xff08;CRUD&#xff09;基础版-CSDN博客 数据库增删改查&#xff08;CRUD&#xff09;进阶版-CSDN博客 数据库的索引-CSDN博客 基本概念 JDBC编程就是通过Java代码来操作数据库 api 数据库是…

基于 Flink CDC 构建 MySQL 的 Streaming ETL to MySQL

简介 CDC 的全称是 Change Data Capture &#xff0c;在广义的概念上&#xff0c;只要是能捕获数据变更的技术&#xff0c;我们都可以称之为 CDC 。目前通常描述的 CDC 技术主要面向数据库的变更&#xff0c;是一种用于捕获数据库中数据变更的技术。CDC 技术的应用场景非常广泛…

【学习记录】从0开始的Linux学习之旅——字符型设备驱动及应用

一、概述 Linux操作系统通常是基于Linux内核&#xff0c;并结合GNU项目中的工具和应用程序而成。Linux操作系统支持多用户、多任务和多线程&#xff0c;具有强大的网络功能和良好的兼容性。基于前面应用与驱动的开发学习&#xff0c;本文主要讲述如何在linux系统上把应用与驱动…

随笔-这都是命吗

我与鹏哥、小付有个小群&#xff0c;前几天&#xff0c;鹏哥在群里发了一个图&#xff0c;是他那个城市准备扶持的高新产业&#xff0c;有元宇宙、量子信息、生物制药、人工智能什么的。 先前的时候鹏哥给我说过&#xff0c;当地准备了六百多亩地&#xff0c;准备发展高新产业…

Labelme2Yolo labelme格式的json标注转yolo格式txt

该工作适用于目标检测工作。 由于labelme标注出的文件是如下图的单个json文件格式&#xff0c;不符合yolo的训练格式&#xff0c;需要转格式。 观察发现labelme标注的json文件中有imageData&#xff0c;还挺大的&#xff0c;查阅后得知是base64后的图片数据&#xff0c;也就是…

多表操作、其他字段和字段参数、django与ajax(回顾)

多表操作 1 基于对象的跨表查 子查询----》执行了两句sql&#xff0c;没有连表操作 2 基于双下滑线的连表查 一次查询&#xff0c;连表操作 3 正向和反向 放在ForeignKey,OneToOneField,ManyToManyField的-related_namebooks&#xff1a;双下滑线连表查询&#xff0c;反向…

深圳锐杰金融:用金融力量守护社区健康

深圳市锐杰金融投资有限公司&#xff0c;作为中国经济特区的中流砥柱&#xff0c;近年来以其杰出的金融成绩和坚定的社会责任立场引人注目。然而&#xff0c;这并非一个寻常的金融机构。锐杰金融正在用自己的方式诠释企业责任和慈善精神&#xff0c;通过一系列独特的慈善项目&a…

定兴县第三实验小学开展“宪法宣传周”系列活动

2023年12月4日是我国第十个国家宪法日&#xff0c;我校集中深入学习宣传宪法&#xff0c;弘扬宪法精神&#xff0c;维护宪法权威&#xff0c;开展“宪法宣传周”系列活动。 宪法主题升旗仪式 五&#xff08;6&#xff09;班薛谨熙同学以《学法懂法 与我同行》为主题做国旗下讲…

【开源】基于JAVA语言的APK检测管理系统

项目编号&#xff1a; S 038 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S038&#xff0c;文末获取源码。} 项目编号&#xff1a;S038&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 开放平台模块2.3 软…

低代码你需要了解一下

低代码的概念可以追溯到1980年代&#xff0c;当时IBM的快速应用程序开发工具&#xff08;RAD&#xff09;被冠以新的名称——低代码&#xff0c;由此&#xff0c;低代码的概念首次面向大众。然而&#xff0c;在近40年的历程中&#xff0c;低代码发展经历了两个阶段&#xff1a;…