【Linux】文件描述符和重定向

news2024/11/22 19:11:35

目录

一、回顾C文件

二、系统文件I/O

2.1 系统调用 open 

2.2 标志位传参

2.3 系统调用 write

2.4 文件描述符fd

2.5 struct file

2.6 fd的分配规则

2.7 重定向

2.7.1 基本原理:

2.7.2 系统调用 dup2

2.8 标准错误


一、回顾C文件

文件 = 内容 + 属性

  1. 对文件的操作:a.对内容操作  b.对属性操作
  2. 内容是数据,属性其实也是数据 —— 存储文件,必须既存储内容,又存储属性数据 —— 默认文件是在磁盘中的。
  3. 要访问一个文件的时候,要先把这个文件打开。
    把含有打开文件的代码(fopen)编译成可执行程序,并将程序运行起来,在执行fopen函数时才会打开这个文件。进程是Linux中最常见的文件打开者。
  4. 文件打开前,是普通的磁盘文件。打开后,文件被加载到内存。
  5. 一个进程可以打开多个文件、多个进程可以打开多个文件。加载到内存中被打开的文件,可能会存在多个。
  6. 加载磁盘上的文件,一定会涉及到磁盘设备,该操作由OS执行。
  7. 操作系统在运行中,可能会打开很多个文件,也需要管理很多文件,就需要“先描述再组织”
  8. 一个文件要被打开,一定要先在内核中形成被打开的文件对象,该对象包含文件的很多属性。例如 struct file
  9. 于是对打开文件的管理,就变成了对链表的增删查改。
  10. 对文件管理的学习,就要研究被打开的文件在内存中的表现,和没有被打开的文件在磁盘中如何存储。

fopen函数

        #include <stdio.h>

        FILE *fopen(const char *filename, const char *mode);

参数:

  • filename:指向包含文件路径的字符串指针。
  • mode:指向包含文件打开模式的字符串指针。

文件打开模式:

  • r:以读取方式打开文件,文件必须存在。
  • w:以写入方式打开文件,如果文件已存在,会将文件长度截断成0,从文件开头处写入;文件不存在则创建文件。类似echo "..." > log.txt
  • a:以追加方式打开文件,写入数据将添加到文件末尾。类似echo "..." >> log.txt
  • +:允许读写文件。
  • b:以二进制模式打开文件(通常用于非文本文件)。
  • t:以文本模式打开文件(默认模式)。

二、系统文件I/O

综上所述,文件是在磁盘中的,要被访问就要先被打开,打开文件的本质是将文件加载到内存里,加载的过程由操作系统完成,因此一个进程需要通过操作系统打开文件。操作系统管理硬件,会提供许多系统调用接口。C语言打开文件的接口,底层一定封装了系统调用接口。
w 和 a 方式打开文件,底层调用的就是open。

2.1 系统调用 open 

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:标志位,指定打开文件的选项。
    O_RDONLY: 只读打开
    O_WRONLY: 只写打开
    O_RDWR : 读,写打开
                       这三个常量,必须指定一个且只能指定一个
    O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
    O_APPEND: 追加写。
    O_TRUNC:如果文件已存在,则截断文件内容。
  • mode:指定文件权限的模式,仅在 O_CREAT 标志被设置时使用。

返回值:

  • 文件成功打开,返回一个非负整数的文件描述符。
  • 文件打开失败,返回 -1,并且可以通过 errno 获取错误信息。

注:

  • open 函数具体使用哪个,和具体应用场景相关。如目标文件不存在,需要open创建,此时第三个参数表示创建文件的默认权限,否则使用两个参数的open。
  • open 创建打开文件时如果flags设置为O_WRONLY | O_CREAT | O_TRUNC,且没有设置mode权限,文件被创建为具有默认权限的文件,通常是 0644。
  • 创建文件时,权限为:mode & ~umask(权限掩码),可使用系统调用umask()设置文件创建时的权限掩码。可以直接设置umask(0),这样设置的mode就是新创建文件的权限。但是这种方法不推荐,推荐使用系统默认的umask。

           #include <sys/types.h>
           #include <sys/stat.h>

           mode_t umask(mode_t mask);

  • fopen的w方式打开相当于open选项的O_WRONLY | O_CREAT | O_TRUNC,即按照写方式打开,如果文件不存在就创建它,打开时会先清空文件内容。

  • fopen的a方式打开相当于open选项的O_WRONLY | O_CREAT | O_APPEND

  • 由此可见,C语言提供的库函数会封装某些底层的系统调用。

  • open创建新文件时先有struct file,再去同步磁盘

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <string.h>    
#include <unistd.h>    
    
    
int main()    
{    
    //FILE *fp = fopen("log.txt","w");    
    //...    
    //fclose(fp);    
    
    //umask(0);    
    int fd = open("log.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);    
    if(fd < 0)    
    {    
        perror("open");                                                                                          
        return 1;    
    }    
    const char* msg = "aaaaaaaa\n";    
    write(fd, msg, strlen(msg));//strlen不需要+1
                                                                                                                 
    int fd1 = open("log1.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd2 = open("log2.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd3 = open("log3.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);
    int fd4 = open("log4.txt",O_WRONLY | O_CREAT | O_TRUNC, 0666);

    printf("fd1 : %d\n",fd1);
    printf("fd2 : %d\n",fd2);
    printf("fd3 : %d\n",fd3);
    printf("fd4 : %d\n",fd4);

    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    return 0;
}

 

2.2 标志位传参

一般标志位是两态的数据(0/1),那么就可以用 flags 中32个不同的比特位传参。这样就可以实现同时传递多个比特位。 

#include <stdio.h>    
    
#define Print1 1        //0001    
#define Print2 (1<<1)   //0010    
#define Print3 (1<<2)   //0100    
#define Print4 (1<<3)   //1000    
    
void Print(int flags)    
{    
    if(flags&Print1) printf("hello 1\n");    
    if(flags&Print2) printf("hello 2\n");    
    if(flags&Print3) printf("hello 3\n");    
    if(flags&Print4) printf("hello 4\n");    
}    
    
int main()    
{    
    Print(Print1);    
    Print(Print1 | Print2);    
    Print(Print1 | Print2 | Print3);    
    Print(Print1 | Print2 | Print3 | Print4);                                                                    
    return 0;    
} 

 

2.3 系统调用 write

write :用于向文件描述符指定的文件或设备写入数据。

       #include <unistd.h>

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

参数:

  • fd:文件描述符,用于指定要写入的文件或设备。
  • buf:指向要写入数据的缓冲区的指针。
  • count:要写入的字节数。

返回值:

  • 写入成功,返回实际写入的字节数。
  • 写入失败,返回 -1,并且可以通过 errno 获取错误信息。

注:

  • 当我们想向一个文件中写入字符串的时候,count的值不需要strlen()+1,因为 \0 结尾是C语言的规定,不是文件的规定!
  • 使用默认的写方式打开文件(O_WRONLY),write写入内容时,没有将文件原内容清空,而是覆盖式的写入。
  • 如果想写入文件时把文件清空,需要在标志位选项上加上O_TRUNC,表示截断文件内容。

2.4 文件描述符fd

文件描述符fd(file descriptor)是一个非负整数,用于标识一个打开的文件。

内存中有许多进程和进程打开的文件,于是操作系统就要解决进程和打开文件对应关系的维护问题!

struct file 是被打开文件的描述结构体。操作系统在内核中,为进程设置了一个指针files,指向结构体 struct files_struct,该结构体包含一个指针数组struct file* fd_array[],数组中的每个元素都是一个指向 struct file 的指针,当进程打开一个文件时(open),OS创建了一个struct file,将该file结构体的地址填入到指针数组中,此时open会向上层返回填入数组位置的下标,这个下标就是文件描述符files_struct就是进程文件描述符表


使用write时,第一个参数就是fd,就可以根据fd找到对应的 struct file。

在上面示例的fd数字为4、5、6、7,(3是第一个fd)没有0、1、2。

原因:

进程在运行时,默认打开3个文件:

  1. 标准输入(键盘)        stdin        0
  2. 标准输出(显示器)    stdout      1
  3. 标准错误(显示器)    stderr       2

它们在进程文件描述符表中以及占据了前3个,所以后续文件从下标3开始。

stdin、stdout、stderr是 FILE* 类型,在C语言中 FILE 是一个结构体类型。因为操作系统访问文件只认文件描述符,所以FILE结构体中也必定要封装文件描述符。结果是FILE结构体中确实有文件描述符,名称为 _fileno

    printf("stdin->fd: %d\n",stdin->_fileno);    
    printf("stdout->fd: %d\n",stdout->_fileno);    
    printf("stderr->fd: %d\n",stderr->_fileno);    
    
    FILE *fp = fopen("log.txt","w");    
    printf("fp->fd: %d\n",fp->_fileno);    
    fclose(fp);   

OS/C语言为什么默认要把0,1,2,stdin,stdout,stderr打开呢?

答:是为了让程序员默认进行标准的输入输出代码编写!

如何理解“一切皆文件”?
在Linux中,所有的输入和输出都被抽象为文件。这意味着无论是从键盘输入、从文件读取,还是向屏幕输出、向文件写入,都使用文件描述符来处理。
那么如何使用文件描述符来处理? ——每个文件描述符对应着一个文件,即对应着一个strcut file。strcut file封装了与文件操作相关的函数指针,如 read、write,分别指向对应硬件的读和写方法(硬件被操作系统“先描述再组织”,每个硬件设备对应的结构体有它自己的读和写方法,因为硬件的作用就是为了实现某些读或者写操作)。从此之后读取硬件不需要使用硬件的读写方法,而是使用 file 结构体中的函数指针调用。这一结构体层称作VFS(虚拟文件系统),用户不再需要关注底层硬件的差异,只用关注文件(因为读写方法全都一样)。

这种操作类似于C++中的多态。虚拟文件系统看作基类,硬件对应子类,基类是一层虚方法,不实现具体的方法,而是由子类实现具体的方法。

补充:

  • 文件操作系统调用:所有的文件操作,如读取(read)、写入(write)、打开(open)、关闭(close)等,都是通过系统调用进行的。这些系统调用与文件操作密切相关。
  • 用户空间和内核空间:文件操作接口在用户空间和内核空间之间提供了一个统一的接口。用户空间程序通过文件描述符和系统调用与内核空间交互,实现文件操作。
  • 设备驱动程序:设备驱动程序是内核的一部分,它们将硬件设备与文件系统中的设备文件关联起来。驱动程序通过文件操作接口与用户空间程序交互。

通过“一切皆文件”的概念,Linux系统提供了一个统一的方式来处理不同的输入和输出,这使得系统更加模块化、易于管理和扩展。需要注意的是,虽然从用户空间的角度来看,所有输入和输出都是通过文件描述符处理的,但在内核中,这些操作的实现可能会依赖于特定的设备驱动程序和硬件接口。

虚拟文件系统(Virtual File System,VFS)是内核的一个抽象层,它提供了一个统一的接口,使得不同的文件系统可以被内核和用户空间应用程序以一致的方式访问。VFS的主要作用是将底层的文件系统实现细节抽象化,从而允许用户空间应用程序和内核中的其他模块无需关心具体文件系统的实现细节。

2.5 struct file

在Linux内核中,struct file 是一个关键的数据结构,用于表示打开的文件。它包含了与文件操作相关的信息,如文件操作函数指针、文件状态、文件访问权限等。struct file 是内核中文件系统抽象层(VFS)的一部分,它为用户空间程序和内核中的其他模块提供了一个统一的接口,用于执行文件操作。

类似于task_struct,struct file 在磁盘中不存在,而是打开文件时,操作系统在内核中创建的struct file的节点,属于内核数据结构,专门用来管理被打开文件。这个文件不止指磁盘文件,还包括磁盘设备本身、键盘显示器等。

struct file 结构体包含了指向文件缓冲区的指针(f_mapping 字段),这些缓冲区用于缓存文件操作的数据,以提高文件访问的效率。 

当内核要读取磁盘中的文件数据,根据冯诺依曼体系结构,需要先把文件数据加载到文件缓冲区(内存)。内核需要读取文件数据时,它会首先检查文件缓冲区中是否已经缓存了所需的数据。

  • 如果读取文件数据时发现数据不在文件缓冲区中,此时发生缺页中断,将磁盘中的数据加载到缓冲区中。
  • 如果数据在缓冲区中,内核可以直接从缓冲区中读取,而无需触发缺页中断。
  • 无论读写,都要先把数据加载到文件缓冲区中。
  • 我们在应用层进行数据的读写本质是将内核缓冲区中的数据进行来回拷贝

文件操作方法集 f_op

f_op 是一个struct file 中的字段,它指向一个 struct file_operations 数据结构。这个数据结构包含了与文件操作相关的函数指针集合。

2.6 fd的分配规则

  • 进程默认已经打开了0,1,2,我们可以直接使用0,1,2进行数据的访问!
  • 文件描述符的分配规则是,寻找最小的,没有被使用的数据的位置,分配给指定的打开文件!(文件描述符可以被复用)
  • 文件关闭时,fd_array中对应的数据被清空。内核会将其标记为可复用,并在下次需要打开文件时重新分配给新的文件。
  • 当一个进程通过 fork 创建一个子进程时,子进程会继承父进程的文件描述符表。子进程可以使用与父进程相同的文件描述符来访问相同的文件。

2.7 重定向

2.7.1 基本原理:

文件描述符1删除时,无法打印内容到显示器。新创建的文件的fd为1,再向1打印就是向这个文件中打印。(这个过程叫做输出重定向)

    close(1);    
    int fd = open("log.txt", O_CREAT | O_WRONLY | O_TRUNC, 0666);    
    if(fd < 0)    
    {    
        perror("open");    
        return 1;    
    }    
    printf("fd: %d\n",fd);    
    printf("stdout->fd: %d\n",stdout->_fileno);                                                                  
    fflush(stdout);    
    close(fd);  

现象:不向显示器打印,而是向文件打印。 

原因: close(1)后,新打开的文件的fd为1,printf函数只认标准输出(stdout),而stdout的_fileno为1,所以printf函数向1号文件描述符对应的文件打印,不管1号文件对应的是不是显示器。
(注:printf("fd: %d\n",fd);   相当于 fprintf(stdout, "fd: %d\n",fd);   )

所以完成重定向功能,只要更改fd_array内容(下标不变)即可。

重定向的本质,其实就是修改文件描述符表特定数组下标的内容。

如果没有 fflush() ,打印的内容不会打印到对应的文件中。因为C语言提供了两种缓冲区:用户级缓冲区和内核级缓冲区。使用printf、fprintf时,要打印的数据先拷贝在用户级缓冲区,然后fflush()再将缓冲区数据拷贝到fd指定文件里。(在后面缓冲区部分讲述)

如果将上面open的选项改为:O_CREAT | O_WRONLY | O_APPEND ,代码的工作就变成了追加重定向

输入重定向如下:(本来应该从键盘上输入)

    close(0);    
    int fd = open("log.txt", O_RDONLY);    
    if(fd < 0)    
    {    
        perror("open");    
        return 1;    
    }    
    
    char buffer[1024];    
    fread(buffer, 1, sizeof(buffer), stdin);    
    printf("%s\n",buffer);    
    close(fd);   

以上的重定向操作都需要关闭原本的文件、再打开新的文件,这种操作有些繁琐而且还容易出错。

2.7.2 系统调用 dup2

Linux中提供了一个系统调用 dup2 ,用于复制一个已存在的文件描述符到另一个已存在的文件描述符。实现文件描述符表级别的数组内容的拷贝

#include <unistd.h>

int dup2(int oldfd, int newfd, int flags);

参数:

  • oldfd:要复制的文件描述符。
  • newfd:目标文件描述符,dup2 会将 oldfd 的内容复制到这个位置。
  • flags:可选参数,用于指定复制行为。通常使用 0,表示复制后关闭 oldfd。

返回值:

  • 成功,dup2 返回新的文件描述符值。
  • 失败,返回 -1,并且可以通过 errno 获取错误信息。

功能:

  • 将 oldfd 指向的文件描述符的内容复制到 newfd 指向的位置,如果 newfd 已经打开,它会先关闭 newfd。这意味着 oldfd 和 newfd 最终指向同一个文件描述符。
    // 打开文件    
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);    
    if (fd == -1) {    
        perror("open failed");    
        return 1;    
    }    
    
    printf("%d\n",stdout->_fileno); 
   
    // 将标准输出重定向到文件描述符    
    if (dup2(fd, stdout->_fileno ) == -1) {    
        perror("dup2 failed");    
        close(fd);    
        return 1;    
    }    
    
    // 关闭原始文件描述符    
    close(fd);                                                                                                   
    
    // 输出内容到文件    
    printf("Hello, World!\n");    

    // 关闭文件描述符    
    close(stdout->_fileno); 

我们向标准输出写入一些内容,这些内容实际上会被写入 log.txt 文件,因为标准输出现在指向这个文件。

2.8 标准错误

在Linux中,标准错误(stderr)通常用于输出错误消息和警告信息,而不是程序的常规输出。

  • 标准错误流被分配文件描述符2
  • 可以通过重定向操作符 2> 或 2>> 将标准错误重定向到文件
  • 标准错误流和标准输出流是独立的,可以分别重定向到不同的目的地。

如果要将标准错误重定向到标准输出,可以使用 2>&1

类似于 ls -a -l > log.txt ,将打印的内容重定向输出到log.txt文件,可以理解成
ls -a -l 1>log.txt ,即把输入到 1(stdout) 的内容输入到 log.txt。那么 2>&1 就是把要输出到2(stderr)的内容重定向输入到1对应的内容中。

./mybin 1 > log.txt 2>&1  或 ./mybin > log.txt 2>&1  
结果:把mybin的标准输入和标准输出都重定向到 log.txt 中

为什么要有标准错误?

答:

  1. 区分正常输出和错误输出:标准错误用于输出程序执行过程中的警告和错误信息,而标准输出用于输出程序的正常输出和结果。这样的区分使得用户和开发者可以更容易地识别和处理程序的错误,而不需要区分正常输出和错误输出。
  2. 多目的地输出:标准错误允许程序将错误输出重定向到不同的目的地,如文件或终端。例如,如果程序的输出被重定向到一个文件,而错误信息仍然需要显示在终端上,可以使用 2>&1 操作符将错误输出重定向到标准输出。
  3. 调试和日志记录:标准错误流通常被用于输出调试信息和日志记录。由于标准错误输出可以被重定向到文件,这使得记录程序执行过程中的错误和警告信息变得更加容易。
  4. 交互式和脚本环境:在交互式环境中,标准错误输出通常会显示在终端上,这有助于用户立即识别和处理程序执行过程中的问题。在脚本环境中,标准错误输出可以被重定向到一个日志文件,以便在脚本执行后查看错误信息。
  5. 多进程和子进程:子进程会继承父进程的标准错误流。这意味着如果父进程的标准错误被重定向,子进程的标准错误也会被重定向到相同的位置。

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

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

相关文章

阿里云OSS配置跨域及域名访问

1、配置跨域 进入对象存储OSS–>OSS存储桶–>数据安全–>跨域设置–>创建规则 2、配置跨域 Etag x-oss-request-id3、配置结果如下 4、数据源配置 切换到数据管理–>静态页面 配置根页面 保存结果如下 5、配置域名访问 绑定域名 添加txt记录 验证绑定 …

【CSP CCF记录】202109-2 非零段划分

题目 过程 思路 参考&#xff1a;http://t.csdnimg.cn/XRKTm STL库用法 unique用法 unique是STL中很实用的函数之一&#xff0c;需要#include&#xff08;感谢各位提醒&#xff09;&#xff0c;下面来简单介绍一下它的作用。 unique的作用是“去掉”容器中相邻元素的重复…

手机配置在线检测工具微信小程序源码

手机配置在线检测工具微信小程序源码&#xff0c;这是一款升级版检测工具&#xff0c;自动检测手机真伪,序列号等。另外还可以给手机检测各项功能是否正常。 由于能检测的项目太多,所以大家到时候自行研究吧。另外支持多做流量主模式,还有外卖CPS,和友情小程序推荐等&#xff…

Unity自定义动画-Animation动画数据-How is “fileIDToRecycleName“ generated

一般美术和程序分工明确的项目 fbx确实是和动画一一对应的&#xff1b; 但一些独立&#xff0c;或者小工作室的项目&#xff0c;就没法保证了&#xff0c;关键还是在于 Unity的 .meta 目录 查找和对比了一下 .fbx 和 .meta&#xff1a; 缓存和不缓存Animation 具体的Animat…

天诚AIoT无线联网智能门锁即将亮相成都安博会、永康门博会

5月上旬&#xff0c;对于江苏新巢天诚智能技术有限公司&#xff08;以下简称“天诚”&#xff09;而言&#xff0c;依旧忙得如火如荼。随着各地人才公寓、公租房、智慧校园类智慧通行与租住新项目的实施、落地与服务&#xff0c;天诚也不忘初心&#xff0c;携全新升级的AIoT全场…

DEV--C++小游戏(吃星星(0.5))

目录 吃星星&#xff08;0.5&#xff09; 该版本简介 DEV--C小游戏(吃星星(0.1)) DEV--C小游戏(吃星星(0.2)) 分部代码 头文件 命名空间变量&#xff08;增&#xff09; 副函数&#xff08;新&#xff0c;增&#xff09; 清屏函数 打印地图函数&#xff08;增&…

d18(169-174)-勇敢开始Java,咖啡拯救人生

目录 特殊文件 .properties 属性文件 读取属性文件 写出属性文件 .xml XML文件 读取XML文件 ​编辑 写出XML文件 约束XML文件 日志技术 Logback 日志级别 特殊文件 .properties 属性文件 每行都是一个键值对 键不能重复 文件后缀一般是.properties 读取属性文件 …

记录一下 log4j的漏洞

目录 背景 bug的产生 bug复现 JNDI 网络安全学习路线 &#xff08;2024最新整理&#xff09; 学习资料的推荐 1.视频教程 2.SRC技术文档&PDF书籍 3.大厂面试题 特别声明&#xff1a; 背景 log4j这次的bug&#xff0c;我相信大家都已经知道了&#xff0c;仅以…

OpenSSL自签证书并基于Express搭建Web服务进行SSL/TLS协议分析

OpenSSL自签证书并基于Express搭建Web服务进行SSL/TLS协议分析 起因 最近在学习安全协议&#xff0c;大多数实验都是基于Windows下IIS&#xff0c;或者Linux下nginx搭建的Web服务&#xff0c;搭建环境和编写配置文件比较麻烦。而且我有多个不同环境的设备&#xff0c;折腾起来…

使用Dockerfile配置Springboot应用服务发布Docker镜像-16

创建Docker镜像 springboot-docker模块 这个应用可以随便找一个即可&#xff0c;这里不做详细描述了。 pom.xml 依赖版本可参考 springbootSeries 模块中pom.xml文件中的版本定义 <dependencies><dependency><groupId>com.alibaba.cloud</groupId>…

EasyExcel导出Excel文件——合并单元格多层级数据导出

合并单元格多层数据导出 思维脑图 代码实现 /*** 导出所有信息** param request 请求体*/ Override public void getWilliamExportList(WilliamReqVo request, HttpServletResponse response) throws Exception {List<SysDictData> dataByType dictDataService.getDic…

添砖Java之路(其五)——封装,String,StringBuilder类。

封装&#xff1a; 封装意义&#xff1a;更好的维护数据&#xff0c;让使用者无需关心如何使用&#xff0c;只需要知道怎么使用。 Java Bean&#xff1a; 然后我们要知道Java Bean(实体类)标准。 1.对于这个类的成员都需要设为私有&#xff0c;而且要对外提供相应Get,Set的接…

WWW服务器搭建(1)——HTTP协议原理篇

目录 一、WWW的相关概念 1.1 WWW的定义 1.2 超文本标记语言HTML 1.3 统一资源定位符URL 1.4 超文本传输协议HTTP 二、HTTP协议工作过程 2.1 DNS解析 2.2 TCP连接过程 2.3 HTTP 请求与响应 2.4 TCP连接断开 三、HTTP请求报文格式 3.1 请求行 3.2 请求头 3.3 空行 …

大语言模型的数据预处理

文章目录 质量过滤敏感内容过滤数据去重 当收集了丰富的文本数据之后&#xff0c;为了确保数据的质量和效用&#xff0c;还需要对数据进行预处理&#xff0c;从而消除低质量、冗余、无关甚可能有害的数据。一般来说&#xff0c;需要构建并使用系统化的数据处理框架&#xff08;…

第十五节:贪心算法(下)

一 、 贪心算法的解题套路实战一&#xff08;最多的会议宣讲场次&#xff09; 1.1 描述 一些项目要占用一个会议室宣讲&#xff0c;会议室不能同时容纳两个项目的宣讲。 给你每一个项目开始的时间和结束的时间 你来安排宣讲的日程&#xff0c;要求会议室进行的宣讲的场次最多。…

校园志愿者管理系统带万字文档

文章目录 校园志愿者管理系统一、项目演示二、项目介绍三、10000字论文参考四、部分功能页面五、部分代码展示六、底部获取项目源码和万字论文参考&#xff08;9.9&#xffe5;带走&#xff09; 校园志愿者管理系统 一、项目演示 校园志愿者管理系统 二、项目介绍 基于Spring…

快速对比 找出2个名单不同之处

import pandas as pd# 读取两个Excel文件 df1 pd.read_excel(1.xlsx) df2 pd.read_excel(2.xlsx)# 检查两个DataFrame的列是否相同 if list(df1.columns) ! list(df2.columns):print("两个Excel文件的列不一致。")print("文件1的列&#xff1a;", df1.co…

免费思维13招之九:时间型思维

免费思维13招之九:时间型思维 免费思维的另一大战略思维——时间型思维。 什么是时间型思维呢?就是在某一个规定的时间内对消费者进行免费,比如一个月中的某一天,或一周中的某一天或一天中的某一个时间段对消费者进行免费。 就在去年,有一个电影院老板弟子,他的电影院营…

基于SSM的“基于协同过滤的在线通用旅游平台网站”的设计与实现(源码+数据库+文档)

基于SSM的“基于协同过滤的在线通用旅游平台网站”的设计与实现&#xff08;源码数据库文档) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SSM 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统主界面 景点信息界面 后台界面 部分源码…

设计循环队列-C语言实现

题目描述 设计循环队列 设计你的循环队列实现。 循环队列是一种线性数据结构&#xff0c;其操作表现基于 FIFO&#xff08;先进先出&#xff09;原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。 循环队列的一个好处是我们可以利用这个队列之前用过的…