【APUE】标准I/O库

目录

1、简介

2、FILE对象

3、打开和关闭文件

3.1 fopen

3.2 fclose

4、输入输出流

4.1 fgetc

4.2 fputc

4.3 fgets

4.4 fputs

4.5 fread

4.6 fwrite

4.7 printf 族函数

4.8 scanf 族函数

5、文件指针操作

5.1 fseek

5.2 ftell

5.3 rewind 

6、缓冲相关

6.1 fflush

6.2 setvbuf  

7、补充

7.1 getline

7.2 临时文件 


1、简介

I/O : input and output,是一切实现的基础

IO分为标准IO(stdio)和系统调用IO(sysio)

  • 系统调用IO根据操作系统的实现方式而定,不同OS有着不同的系统调用IO
  • 标准IO对不同OS下的系统调用IO进行了封装,从而提供了一套不同OS下相同IO实现库函数
系统调用IO与标准IO示意图

2、FILE对象

FILE对象通常是一个结构体,包含了标准I/O库为管理该流需要的所需要的所有信息

FILE类型贯穿始终,可以理解为FILE就代表流

一个进程默认打开了三个流,分别是标准输入 stdin、标准输出 stdout 和标准错误 stderr

一个进程默认打开1024个流,可通过如下命令查看LINUX控制shell程序的资源:

3、打开和关闭文件

3.1 fopen

FILE *fopen(const char *pathname, const char *mode);
// The fopen() function opens the file whose name is the string pointed to by pathname and associates a stream with it.
  • pathname — 字符串,表示要打开的文件名称
  • mode — 字符串,表示文件的访问模式,该指针指向以下面字符开头的字符串:
mode描述(man手册的直接翻译)
"r"为读取而打开文本文件。流定位到文件开头
"r+"为读写而打开。流定位到文件开头
"w"将文件截断至0长,或为写入而创建文本文件。流定位到文件开头
"w+"为读写而打开。文件不存在则创建,否则截断。流定位到文件开头
"a"为追加(在文件尾写)而打开。文件不存在则创建。流定位到文件末尾
"a+"为读和追加而打开。文件不存在则创建。读取文件的初始位置是文件的开头,但输出总是追加到文件的结尾

只有模式 "r" 和 "r+" 要求文件必须存在,其他模式都可以创建文件;

mode也可以包含字母 b,放在最后或者中间,表示二进制流。例如 "rb"、"r+b";

打开成功返回一个 FILE 指针,否则返回 NULL 并设置全局变量 errno 来标识错误。该全局变量在头文件 errno.h 中声明:(只展示部分)

#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */

为了通过全局变量 errno 的值得到对应的错误提示信息,可以利用C标准中定义的如下两个函数

#include <stdio.h>
void perror(const char *s);
// 在库函数中有个errno变量,每个errno值对应着以字符串表示的错误类型。当你调用“某些”函数出错时,该函数已经重新设置了errno的值
// perror函数只是将你输入的一些信息和errno所对应的错误一起输出
#include <string.h>
char *strerror(int errnuum);
// 搜索错误号errnum,并返回一个指向错误消息字符串的指针

代码示例:


fopen函数解析:

由函数原型可知,fopen函数返回的是一个FILE类型的指针,FILE是一个结构体,由typedef进行了重命名,而指针实际上是指向结构体的指针

关键问题:指针指向的哪个区?也就是说FILE结构体放在内存的哪一块?是堆,是栈,还是静态区?

换句话说,在fopen函数内部,FILE对象是如何创建的?

如果创建在栈区,当程序退出这个块时,释放刚才为变量tmp分配的栈内存,因此,会返回一个被释放的内存地址,错误

FILE *fopen(const char *pathname, const char *mode)
{FILE tmp;// 给结构体成员赋值初始化tmp.xxx = xxx;tmp.yyy = yyy;...return &tmp;
}

如果创建在静态区,假如多次调用 fopen 函数,也只能存在一个FILE实例(因为只有这一个内存区供指针指向),最后一次的FILE结构体内容会把前一次的结果覆盖掉,错误

FILE *fopen(const char *pathname, const char *mode)
{static FILE tmp;// 给结构体成员赋值初始化tmp.xxx = xxx;tmp.yyy = yyy;...return &tmp;
}

创建在堆区,这是正确的,此时 tmp 具有动态存储期,从调用 malloc 分配内存到调用 free 释放内存为止,而 free 就在 fclose 函数中被调用

FILE *fopen(const char *pathname, const char *mode)
{FILE * tmp = malloc(sizeof(FILE));// 给结构体成员赋值初始化tmp->xxx = xxx;tmp->yyy = yyy;...return tmp;
}

3.2 fclose

int fclose(FILE *stream);
// The fclose() function flushes the stream pointed to by stream (writing any buffered output data using fflush(3)) and closes the underlying file descriptor.
  • stream — 这是指向 FILE 对象的指针,该 FILE 对象指定了要被关闭的流
  • 如果流成功关闭,则该方法返回零。如果失败,则返回 EOF

一般来说,fopen 和 fclose 一一对应,fopen 中为 FILE 对象分配动态内存,fclose 中利用 free 释放所分配的动态内存

代码示例:

4、输入输出流

下面仅给出部分字符和字符串的输入输出流操作,详细见 man 手册

  • man 3 fgetc
  • man 3 fputc
  • man 3 fread
  • man 3 fwrite

4.1 fgetc

int fgetc(FILE *stream);
// fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, 
// or EOF on end of file or error.

功能:从指定流中获取下一个字符 

还有几个类似功能的:

int getc(FILE *stream);int getchar(void);

getchar 等同于 getc(stdin);

getc 和 fgetc 使用方式完全相同,fgetc 通过函数实现,而 getc 通过宏定义实现;

fgetc 中的 f 代表的是 function 的意思,而不是 file 的意思;

宏只占用编译时间,不占用调用时间,而函数相反,因此内核的实现通常使用宏来定义函数,因为调用函数的时间通常长于调用宏;

4.2 fputc

int fputc(int c, FILE *stream);
// fputc() writes the character c, cast to an unsigned char, to stream.

功能:将指定字符写入指定流 

还有几个类似功能的:

int putc(int c, FILE *stream);int putchar(int c);

putchar 等同于 putc(c, stdout);

putc 和 fputc 使用方式完全相同,fputc 通过函数实现,而 putc 通过宏定义实现;

fputc 中的 f 代表的是 function 的意思,而不是 file 的意思;

宏只占用编译时间,不占用调用时间,而函数相反,因此内核的实现通常使用宏来定义函数,因为调用函数的时间通常长于调用宏;

代码示例:实现一个拷贝文件的功能

将文件 src 拷贝为 dest 

./mycpy src dest

实现代码如下:

使用方法:

diff 对两个文件内容进行对比,如果两个文件完全相同,则什么也不输出

4.3 fgets

char *fgets(char *s, int size, FILE *stream);
// fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s.
// Reading stops after an EOF or a newline. 
// If a newline is read, it is stored into the buffer. 
// A terminating null byte ('\0') is stored after the last character in the buffer.

功能:从指定流中读取批量字符

  • s:指向一个 buffer 缓冲,用于储存从指定流中读取到的字符
  • size:用于限制每次读取的字符个数最多 size - 1 个
  • stream:指定流

fgets 读取结束的条件,满足其一即可:

  • 读到 size-1 个字符时停止
  • 读到换行符 '\n' 时停止,换行符会被存进缓冲
  • 读到文件末尾 EOF

读取结束后,会往读取进 buffer 的最后一个字符后,再添加一个 '\0' ;

如果成功读取到字符,返回 s;

如果发生错误或者什么字符也没读取到,返回 NULL;

任何一个非空文件,末尾都有一个换行符 '\n'

#define SIZE 5
char buf[SIZE]; // 栈上的动态内存
fgets(buf, SIZE, stream);如果stream = "abcde"
则buf = "abcd\0"(读到size-1),文件指针指向e如果stream = "ab"
则buf = "ab\n\0"(读到换行符),文件指针指向EOF极端的情况:
如果stream = "abcd"
则需要fgets读取两次才能读完
第一次读取的为"abcd\0"(读到SIZE-1),指针指向'\n'
第二次读取的为"\n\0"(读到换行符),指针指向EOF

4.4 fputs

int fputs(const char *s, FILE *stream);
// fputs() writes the string s to stream, without its terminating null byte ('\0').

功能:将批量字符(即字符串)写入流

写入流的不包括末尾空字符 '\0'

4.5 fread

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// The  function fread() reads nmemb items of data, 
// each size bytes long, 
// from the stream pointed to by stream, 
// storing them at the location given by ptr.
  • stream — 这是指向 FILE 对象的指针,表示从该文件对象读取;
  • nmemb — 待读取元素的个数;
  • size — 读取的每个元素的大小,以字节为单位;
  • ptr — 将读取到的元素放进 ptr 所指的位置;

函数返回成功读取的元素的数目,如果出错或者达到 EOF,则返回值可能少于 nmemb

示例:

fread(buf, size, nmemb, fp);// 情况1:数据量足够
// 情况2:文件只有5个字节// 读10个对象,每个对象1个字节
fread(buf, 1, 10, fp);// 情况1:
// 第一次读:返回10(读到10个对象),读到10个字节
// 情况2:
// 第一次读:返回5(读到5个对象),读到5个字节//--------------------------------// 读1个对象,每个对象10个字节
fread(buf, 10, 1, fp);// 情况1:
// 第一次读:返回1(读到1个对象),也读到10个字节
// 情况2:
// 第一次读:返回0(读不到1个对象,因为1个对象要10字节,而文件只有5个字节)

 因此建议单字节读取

4.6 fwrite

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
// The function fwrite() writes nmemb items of data, 
// each size bytes long, 
// to the stream pointed to by stream, 
// obtaining them from the location given by ptr.
  • ptr — 从 ptr 所指的内存空间获取待写入元素;
  • nmemb — 写入元素的个数;
  • size — 写入的每个元素的大小,以字节为单位;
  • stream —  这是指向 FILE 对象的指针,表示将元素写入到 stream 输出流;

函数返回成功写入的元素的数目。如果该数字与 nmemb 参数不同,则会显示一个错误。

代码示例:用 fread 和 fwrite 代替 fgtec 和 fputc:

4.7 printf 族函数

#include <stdio.h>int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);    // write at most size bytes (including the terminating null byte ('\0')) to str
  • printf:发送格式化输出到标准输出流 stdout;
  • fprintf:发送格式化输出到流 stream 中。可以实现格式化输出的重定向;
  • sprintf:发送格式化输出到 str 所指内存。它能够将多种数据类型(整型、字符型)的数据综合为字符串类型;
  • snprintf:发送格式化输出到 str 所指内存。它能够将多种数据类型(整型、字符型)的数据综合为字符串类型,最多发送 size 个字符(包括末尾的 '\0')

辅助函数:将字符串初始部分转化为整数

#include <stdlib.h>// convert a string to an integerint atoi(const char *nptr);    // The atoi() function converts the initial portion of the string pointed to by nptr to int.
long atol(const char *nptr);
long long atoll(const char *nptr);

4.8 scanf 族函数

#include <stdio.h>int scanf(const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);

功能:按照格式说明符读取并解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置 

三者区别: 

The scanf() function reads input from the standard input stream stdin, fscanf() reads input  from the  stream pointer stream, and sscanf() reads its input from the character string pointed to by str. 

5、文件指针操作

5.1 fseek

#include <stdio.h>int fseek(FILE *stream, long offset, int whence);// The  fseek() function sets the file position indicator for the stream pointed to by stream.

功能:设置文件位置指针指向

  • stream — 这是指向 FILE 对象的指针,该 FILE 对象标识了流
  • offset — 这是相对 whence 的偏移量,以字节为单位
  • whence — 这是表示开始添加偏移 offset 的位置。它一般指定为下列常量之一:
常量描述
SEEK_SET文件的开头
SEEK_CUR文件位置指针当前所在位置
SEEK_END文件的末尾EOF,即文件中最后一个字符的下一个位置

如果成功,则该函数返回零,否则返回非零值

文件位置指针是什么?

  • 文件位置指针用来指示文件中的某个位置,因此在程序中进行读写操作时,位置指针也会随着文件的读写操作而改变
  • 在进行读写操作时,位置指针会自动更新到下一个读写的位置。 例如,当进行读操作时,位置指针会自动更新到下一个可读的位置;当进行写操作时,位置指针会自动更新到下一个可写的位置

5.2 ftell

long ftell(FILE *stream);

功能: 返回文件位置指针所指位置(从文件起始位置开始,并以字节为单位度量,相对起始位置的偏移)

5.3 rewind 

void rewind(FILE *stream);

功能:设置文件位置指针的位置为给定流 stream 的文件的开头 

使用时功能等同于:

(void) fseek(stream, 0L, SEEK_SET)

fseek 和 ftell 函数功能详解:

fseek 和 ftell 中偏移offset的修饰类型是 long,因此只能对2G左右大小的文件进行操作,否则会超出long的范围

fseeko 和 ftello 则将偏移的修饰类型使用typedef定义为offset_t,具体类型交由系统决定,因此不存在文件大小的限制。但是这两个函数不是C标准库函数,而是隶属于POSIX标准(POSIX是标准C库的超集,或者说,C库是普通话,而POSIX是方言)

代码示例:求文件的有效字节数

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>int main(int argc, char **argv){FILE *fp;if(argc < 2) {fprintf(stderr, "Usage...\n");exit(1);}fp = fopen(argv[1], "r");if(fp == NULL) {perror("fopen()");exit(1);}// 将指针定位在文件末尾fseek(fp, 0, SEEK_END);printf("%ld\n", ftell(fp));exit(0);
}

6、缓冲相关

先看一个现象

 

发现 while 循环前的那行字符串并没有显示! 

原因:对于标准输出,输出缓冲区刷新的时机:

  • 输出缓冲区满
  • 或者遇到换行符\n
  • 强制刷新,或者进程结束

因此,上述 while 循环前的那行只是进入输出缓冲区了,并没有冲洗

标准 I/O 提供缓冲的目的是为了减少使用系统调用 read 和 write 的次数,增加程序的吞吐量

6.1 fflush

#include <stdio.h>int fflush(FILE *stream);

功能:冲洗缓冲区

  • 如果参数为 stream 为 NULL,则冲洗所有的已打开的流
  • 如果成功,该函数返回零值。如果发生错误,则返回 EOF,且设置错误标识符(即 feof)

术语冲洗(flush)说明标准I/O缓冲区的写操作。缓冲区可由标准I/O例程自动地冲洗(例如, 当填满一个缓冲区时),或者可以调用函数 fflush 冲洗一个流。值得注意的是,在UNIX环境中,flush有两种意思。在标准I/O库方面,flush(冲洗)意味着将缓冲区中的内容写到磁盘上(该缓冲区可能只是部分填满的)。在终端驱动程序方面,flush(刷清)表示丢弃已存储在缓冲区中的数据。

可行的一些修改方式:

  • 标准输出遇到换行符 '\n' 自动冲洗

 

  • 通过 fflush 手动冲洗 stdout

即可得到期望的输出


标准I/O缓冲的分类(即除了手动 fflush 以外,不同类的缓冲有不同的默认冲洗(标准IO指写入磁盘)时机):

  • 全缓冲(块缓冲):在全缓冲的情况下,在填满标准I/O缓冲区后,才进行冲洗
  • 行缓冲:行缓冲指的是当遇到换行符时,或者缓冲区已经满了(一般1024字节),执行冲洗
  • 无缓冲:不会填缓冲区,可以理解为立即冲洗

不同的标准I/O有默认的缓冲类别 

  • 磁盘上的文件默认是全缓冲的
  • 标准输入和标准输入默认是行缓冲的
  • 一般指向终端设备的流默认是行缓冲,而指向文件时,则默认是全缓冲
  • 为了立即显示错误信息,标准错误默认是无缓冲的

关于缓冲这段的 man 手册:

6.2 setvbuf  

int setvbuf(FILE *stream, char *buf, int mode, size_t size);
// The setvbuf() function may be used on any open stream to change its buffer

功能: 用于改变流的缓冲类别(即改变流在不调用 fflush 的情况下的冲洗时机)

  • stream — 这是指向 FILE 对象的指针,该 FILE 对象标识了一个打开的流
  • buf — 这是用户指定的用于存缓冲内容的位置。如果设置为 NULL,该函数会自动分配一个指定大小的缓冲空间
  • size — the buf argument should point to a buffer at least size bytes long
  • mode — 这指定了文件缓冲的类别:
mode描述
_IOFBF全缓冲
_IOLBF行缓冲
_IONBF无缓冲

7、补充

7.1 getline

之前介绍的函数,都不能获得完整的一整行(有缓冲区大小的限制),而下面介绍的getline函数则可以动态分配内存,当装不下完整一行时,又会申请额外的内存来存储

getline会生成一个包含一串从输入流读入的字符的字符串,直到以下情况发生会导致生成的此字符串结束:

  • 到文件结束
  • 遇到函数的定界符
  • 输入达到最大限度
#define _GNU_SOURCE // 通常将这种宏写在makefile中,现在的编译器没有了该宏,直接使用即可
#include <stdio.h>
ssize_t getline(char **lineptr, size_t *n, FILE *stream);

功能:用于从流中读取完整的行 

要理解这些参数的含义,必须要知道 getline 的工作原理

这样再对照 getline 的声明,就知道各种参数和返回值的含义了

ssize_t getline(char **lineptr, size_t *n, FILE *stream);
  • lineptr:用于存放开辟空间的首地址,*lineptr 指向所开辟空间
  • n:用于存放开辟空间的字节数,*n 为开辟空间的字节数
  • 返回值为读取到的字符数,读取失败返回 -1 

注意 man 手册中的一句特殊的使用要求:

If *lineptr is set to NULL and *n is set 0 before the call, then getline() will allocate a buffer for storing the line.  This buffer should be freed by the user program even if getline() failed.

使用示例:

注意区分开辟空间的字节数和读取到的字符数! 

7.2 临时文件 

临时文件产生的问题:

  • 如何命名不冲突
  • 如何保证及时销毁

tmpnam:生成并返回一个有效的临时文件名,该文件名之前是不存在的。如果 str 为空,则只会返回临时文件名。

存在并发问题,可能会产生两个或多个名字相同的临时文件

可能两个不同进程运行该函数时,检查文件名后,生成文件名前发生了进程切换

#include <stdio.h>
char *tmpnam(char *s);
  • s — 这是一个指向字符数组的指针,其中,临时文件名将被存储为 C 字符串
  • 返回一个指向 C 字符串的指针,该字符串存储了临时文件名。如果 s 是一个空指针,则该指针指向一个内部缓冲区,缓冲区在下一次调用函数时被覆盖
  • 如果 s 不是一个空指针,则返回 s。如果函数未能成功创建可用的文件名,则返回一个空指针

另一个函数:

tmpfile:以二进制更新模式(wb+)创建临时文件。被创建的临时文件会在流关闭的时候或者在程序终止的时候自动删除。

该文件没有名字(匿名文件),函数只返回指向FILE的指针,因此不存在命名冲突的问题,同时会自动删除,因此可以及时销毁。

#include <stdio.h>
FILE *tmpfile(void);
  • 如果成功,该函数返回一个指向被创建的临时文件的流指针。如果文件未被创建,则返回 NULL

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试/测试开发丨学会与 AI 对话,高效提升学习效率

点此获取更多相关资料 简介 ChatGPT 的主要优点之一是它能够理解和响应自然语言输入。在日常生活中&#xff0c;沟通本来就是很重要的一门课程&#xff0c;沟通的过程中表达越清晰&#xff0c;给到的信息越多&#xff0c;那么沟通就越顺畅。 和 ChatGPT 沟通也是同样的道理&…

Java“牵手”ebay商品详情数据,ebay商品详情API接口,ebayAPI接口申请指南

天猫平台商品详情接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取天猫商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片等详细信息 。 获取商品详情接口API是一种用于获取电商平台上商品详情数据的接口&#xff0c;通过…

Java多线程4种拒绝策略

文章目录 一、简介二、AbortPolicy拒绝策略A. 概述B. 拒绝策略实现原理C. 应用场景D. 使用示例 三、CallerRunsPolicy拒绝策略A. 概述B. 拒绝策略实现原理C. 应用场景D. 使用示例 四、DiscardPolicy拒绝策略A. 概述B. 拒绝策略实现原理C. 应用场景D. 使用示例 五、DiscardOldes…

微信小程序AI类目-深度合成-AI问答/AI绘画 互联网信息服务算法备案审核通过教程

近期小程序审核规则变化后&#xff0c;很多使用人类小徐提供的chatGPT系统的会员上传小程序无法通过审核&#xff0c;一直提示需要增加深度合成-AI问答、深度合成-AI绘画类目&#xff0c;该类目需要提供互联网信息服务算法备案并上传资质&#xff0c;一般对企业来说这种务很难实…

ARMv7-A 那些事 - 2.通用寄存器与流水线

By: Ailson Jack Date: 2023.09.10 个人博客&#xff1a;http://www.only2fire.com/ 本文在我博客的地址是&#xff1a;http://www.only2fire.com/archives/154.html&#xff0c;排版更好&#xff0c;便于学习&#xff0c;也可以去我博客逛逛&#xff0c;兴许有你想要的内容呢。…

Visual Studio 2019 简单安装教程

思路 官方页面下载 – 安装Visual Studio Installer – 安装Visual Studio 2019 下载 打开页面&#xff1a;Visual Studio 2019 生成号和发布日期 | Microsoft Learn 点击需要的版本&#xff0c;跳转后会开始下载在线安装包&#xff0c;这里选择第一个Community版本 安装 …

SpringMVC(一)

1.SpringMVC简介 1.1 什么是MVC MVC是一种软件架构的思想&#xff0c;将软件按照模型、视图、控制器来划分 M:Model,模型层&#xff0c;指工程中的JavaBean,作用是处理数据 JavaBean分为两类&#xff1a; 一类称为实体类Bean:专门存储业务逻辑的&#xff0c;如Student、Us…

一篇博客教会您SpringMVC文件上传、下载,多文件上传及工具jrebel的使用

目录 一.文件上传 二.文件下载 三.多文件上传 四&#xff0c;jrebel的介绍 前言&#xff1a; 我们之前已经实现了SpringMVC的增删改查&#xff0c;今天这一篇博客教会您SpringMVC文件上传、下载&#xff0c;多文件上传及工具jrebel的使用&#xff0c;希望这篇博客能够给正在…

图解系列 图解Kafka之Producer

开局一张图&#xff0c;其他全靠吹 发送消息流程如下&#xff1a; 1.初始化流程 指定bootstrap.servers&#xff0c;地址的格式为 host:port。它会连接bootstrap.servers参数指定的所有Broker&#xff0c;Producer启动时会发起与这些Broker的连接。因此&#xff0c;如果你为这…

TCP的滑动窗口协议有什么用?

分析&回答 滑动窗口协议&#xff1a; TCP协议的使用维持发送方/接收方缓冲区 缓冲区是 用来解决网络之间数据不可靠的问题&#xff0c;例如丢包&#xff0c;重复包&#xff0c;出错&#xff0c;乱序 在TCP协议中&#xff0c;发送方和接受方通过各自维护自己的缓冲区。通…

批量采集的时间管理与优化

在进行大规模数据采集时&#xff0c;如何合理安排和管理爬取任务的时间成为了每个专业程序员需要面对的挑战。本文将分享一些关于批量采集中时间管理和优化方面的实用技巧&#xff0c;帮助你提升爬虫工作效率。 1. 制定明确目标并设置合适频率 首先要明确自己所需获取数据的范…

记录在windows下安装MySQL所遇到的各种坑

1.下载 从官网下载installer 然后开始选择要安装的组件 安装了很久进度都是0&#xff0c;无奈点击show detail以后发现&#xff0c;webclient异常&#xff0c;最后是将链接地址复制到迅雷才成功下载的 等迅雷下载完成以后&#xff0c;会看到有如下2个新msi文件 msi都是windows…

Python:安装Flask web框架hello world

安装easy_install pip install distribute 安装pip easy_install pip 安装 virtualenv pip install virtualenv 激活Flask pip install Flask 创建web页面demo.py from flask import Flask app Flask(__name__)app.route(/) def hello_world():return Hello World! 2023if _…

【Go基础】编译、变量、常量、基本数据类型、字符串

面试题文档下链接点击这里免积分下载 go语言入门到精通点击这里免积分下载 编译 使用 go build 1.在项目目录下执行 2.在其他路径下编译 go build &#xff0c;需要再后面加上项目的路径&#xff08;项目路径从GOPATH/src后开始写起&#xff0c;编译之后的可执行文件就保存再…

【Mybatis】Mybatis的工作原理

目录 工作原理图 使用 MyBatis 操作数据库通常需要以下几个步骤&#xff1a; 1、配置数据库连接信息&#xff1a; 2、定义数据表对应的实体类&#xff1a; 3、编写 SQL 映射文件&#xff1a; 4、配置 MyBatis 映射文件&#xff1a; 5、创建 MyBatis 的 SqlSessionFactory&…

基于SSM的宿舍管理系统【附源码文档】

基于SSM的宿舍管理系统【附源码文档】 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatis工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 【主要功能】 角色&#xff1a;管理员、宿舍管理员、学生 管理员&#xff1a;院系信息、班级信…

leetcode872. 叶子相似的树(java)

叶子相似的树 题目描述递归 题目描述 难度 - 简单 leetcode - 872. 叶子相似的树 请考虑一棵二叉树上所有的叶子&#xff0c;这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。 举个例子&#xff0c;如上图所示&#xff0c;给定一棵叶值序列为 (6, 7, 4, 9, 8) 的树。 如果…

Undefined symbols for architecture arm64

解决问题之前&#xff0c;先了解清晰涉及到的知识点&#xff1a; iOS支持的指令集包含&#xff1a;armv6、armv7、armv7s、arm64&#xff0c;在项目TARGETS---->Build Settings--->Architecturs 可以修改对应的指令集&#xff0c;目前Standard Architectures(arm64, arm…

SSM - Springboot - MyBatis-Plus 全栈体系(五)

第二章 SpringFramework 四、SpringIoC 实践和应用 2. 基于 XML 配置方式组件管理 2.5 实验五&#xff1a;高级特性&#xff1a;FactoryBean 特性和使用 2.5.1 FactoryBean 简介 FactoryBean 接口是Spring IoC容器实例化逻辑的可插拔性点。 用于配置复杂的Bean对象&#x…

精益制造、质量管控,盛虹百世慧共同启动MOM(制造运营管理)

百世慧科技依托在电池智能制造行业中的丰富经验&#xff0c;与盛虹动能达成合作&#xff0c;为其提供MOM制造运营管理平台&#xff0c;并以此为起点&#xff0c;全面提升盛虹动能的制造管理水平与运营体系。 行业困境 中国动力电池已然发展为全球最大的电池产业&#xff0c;但…