2023-07-29:华清远见嵌入式2017年线下班:文件IO笔记
这里写目录标题
- 华清远见嵌入式2017年线下班:文件IO笔记
- 文件权限
- 文件IO
- 文件创建和打开操作
- 文件关闭操作
- 出错处理
- 创建设备文件 || create || 老师自己忘了
- 文件读操作
- 练习:计算文件的大小?
- 文件写操作
- 练习:打开file1和file2,将file1中的内容拷贝到file2,实现文件拷贝功能
- 文件定位操作
- 练习:通过lseek计算文件的大小
- 空洞与非空洞文件 (Linux)
- 作业:打开file1,file2,file3 ,将file1前一半内容拷贝到file2,后一半内容拷贝file3```cpp
- 标准IO
- 文件读操作fopen
- 读写流
- 练习: 循环的从标准输入中获取字符(如果是数字则显示,其他的就不显示)
- 练习:要求输入字符,将大写字母转换成小写,小写字母转换成大写。
- 行IO
- 练习:获取以个文件的行数。
- 检查文件出错函数
- 直接IO
- 流的格式化输入输出
- 流的定位
- 获取时间
- 文件信息
- 硬链接和软连接
- 获取用户相关信息
- 获取用户分组
- 目录操作函数
- 库
- 静态库特点
- 共享库的特点
- 如何找到共享库
华清远见嵌入式2017年线下班:文件IO笔记
文件权限

(一)第一个字段表示文件类型 和 文件权限。
第1个字母表示文件类型:d表示目录类型。其他的linux 文件类型以下七种:
(二)其他6个字母表示分别表示属主权限,属组权限,其他用户权限。
权限构成
文件权限由三个部分构成,分别为
文件所有者 User
同组用户 Group
其他用户 Other
每个部分可以用一个数字或三个字母表示:
读取的权限等于4,用r表示
写入的权限等于2,用w表示
执行的权限等于1,用x表示
数字解释
rwx 属性:4+2+1 = 7;
rw- 属性:4+2 = 6;
r-x 属性:4+1 = 7。
以755为例:
1-3 位 7:等于4+2+1,rwx,所有者具有读取、写入、执行权限;
4-6 位 5:等于4+1+0,r-x,同组用户具有读取、执行权限但没有写入权限;
7-9 位 5:等于4+1+0,r-x,其他用户具有读取、执行权限但没有写入权限。
444 r–r–r–
600 drw-------
644 drw-r–r–
666 drw-rw-rw-
700 drwx------
744 drwxr–r–
755 drwxr-xr-x
777 drwxrwxrwx
文件IO
类Linux系统下使用
文件创建和打开操作
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
函数功能:打开、创建文件
函数参数: pathname : 需要打开文件的文件名,文件名长度不能超过255。 flags : 打开方式
O_RDONLY:只读方式打开文件。
O_WRONLY:可写方式打开文件。
O_RDWR:读写方式打开文件。
O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限(读写执行权限)。
O_EXCL:如果使用
O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。
O_TRUNC:如文件已经存在,那么打开文件时先删除文件中原有数据。
O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行。//write 文件开始位置开始写
文件关闭操作
#include <unistd.h>
int close(int fildes);
函数参数:
fildes : 需要关闭文件的文件描述符
返回值:成功 0 失败 -1
例1:以所有者只写方式(参数O_WRONLY)打开文件(mode=64),如果文件不存在则创建(参数O_CREAT),如果文件存在则清空(参数O_TRUNC)。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>/*
以所有者只写方式(参数O_WRONLY)打开文件(mode=64),
如果文件不存在则创建(参数O_CREAT),
如果文件存在则截短(参数O_TRUNC)
*/int main(int argc, const char *argv[])
{int fd;// 不指定umask生成的test.txt文件的权限是0664,指定了就是0666umask(0); // 在当前目录创建"test.txt"// 0666 = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH// fd = open("test.txt", // O_WRONLY | O_CREAT | O_TRUNC , // S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,0666);if(-1 == fd){perror("open failed");// 后面会自动接perror对应的错误信息}close(fd);//1024return 0;
}
在终端中执行,使用命令 ./open
如果存在text.txt文件,perror则会报错,自动提示错误信息

文件权限还可以使用宏进行表示

出错处理
#include <string.h>
char * strerror(int errnum);
全局错误码errno 是记录系统的最后一次错误代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h> //一定要加这个头文件
/*
以所有者只写方式(参数O_WRONLY)打开文件(mode=64),
如果文件不存在则创建(参数O_CREAT),
如果文件存在则截短(参数O_TRUNC)
*/
int main(int argc, const char *argv[])
{int fd;// 不指定umask生成的test.txt文件的权限是0664,指定了就是0666umask(0); // 在当前目录创建"test.txt"fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,0666);if(-1 == fd){print("%s\n", strerror(errno));// errno是记录系统的最后一次错误代码}close(fd);//1024return 0;
}

创建设备文件 || create || 老师自己忘了
cat /proc/devices 查看当前系统支持的驱动

主设备号
usb 字符设备
180 设备号
int creat(const char *pathname, mode_t mode);
等价于open(pathname,
O_CREAT|O_WRONLY|O_TRUNC, mode)
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{int fd;fd = creat("test.txt", 0666); // open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);if(-1 == fd){perror("creat");}close(fd);//1024return 0;
}
文件读操作
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
函数功能:从一个已经打开的文件中读取内容函数参数:fd : 文件描述符buf : 用来存放读到的内容的缓冲区首地址count :读取的字节数返回值:成功: 返回实际读取的字节数(0 读到文件末尾)失败: -1 并设置errno
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd;char buf[1024] = "\0"; // 可以人为指定,不能超过1024字节int count;fd = open("test.txt", O_RDONLY | O_CREAT , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);if(-1 == fd){perror("open failed");}// 每次读取5个字节while((count = read(fd, buf, 5)) > 0) // 不能 >={if(-1 == count){perror("read failed");return -1;}printf("count = %d : %s\n", count, buf); //count = 5}close(fd);//1024return 0;
}
练习:计算文件的大小?
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd;char buf[1024] = "\0";int count = 0 , sum = 0;fd = open("test.txt", O_RDONLY | O_CREAT , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);if(-1 == fd){perror("open failed");}while((count = read(fd, buf, 5)) > 0){if(-1 == count){perror("read failed");return -1;}sum = sum + count;}printf("The size of file is %d\n", sum);close(fd);//1024return 0;
}
文件写操作
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
函数功能:向已经打开的文件中写内容
函数参数:fd : 文件描述符buf : 写缓冲缓冲区首地址count : 指定写入的字节数
返回值:成功:实际写到文件的字节数失败:-1
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd;char buf[1024] = "\0";char writebuf[1024] = "xxxxx";int count;// fd = open("test.txt", O_RDWR | O_CREAT | O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);// 没有O_APPEND,直接覆盖fd = open("test.txt", O_RDWR | O_CREAT , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if(-1 == fd){perror("open failed");}count = read(fd, buf, 1);if(-1 == count){perror("read failed");return -1;}printf("count = %d : %s\n", count, buf);count = write(fd, writebuf, strlen(writebuf));if(-1 == count){perror("write failed");return -1;}printf("count = %d\n", count);// 这里读了一个字符,所以偏移了1count = read(fd, buf, 1); if(-1 == count){perror("read failed");return -1;}printf("count = %d : %s\n", count, buf);close(fd);//1024return 0;
}
text.txt里原来的内容是 “abcdefghijk”
输出:文件初始read了一个字符,位移在第一位,往后覆盖 xxxxx
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd;char buf[1024] = "\0";char writebuf[1024] = "xxxxx";int count;// 有O_APPENDfd = open("test.txt", O_RDWR | O_CREAT | O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);if(-1 == fd){perror("open failed");}count = read(fd, buf, 1);if(-1 == count){perror("read failed");return -1;}printf("count = %d : %s\n", count, buf);count = write(fd, writebuf, strlen(writebuf));if(-1 == count){perror("write failed");return -1;}printf("count = %d\n", count);count = read(fd, buf, 1);if(-1 == count){perror("read failed");return -1;}printf("count = %d : %s\n", count, buf);close(fd);//1024return 0;
}
输出:在下一行追加 xxxxx
练习:打开file1和file2,将file1中的内容拷贝到file2,实现文件拷贝功能
diff -s file1 file2 (比较两个文件是否一致)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd1, fd2;char buf[1024] = "\0";int count;if(argc < 3){printf("command error <source file> <dest file>!\n");return -1;}// 从命令行参数获得文件名fd1 = open(argv[1], O_RDONLY);if(-1 == fd1){perror("open file1 failed");return -1;}fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);if(-1 == fd2){perror("open file2 failed");return -1;}while((count = read(fd1, buf, sizeof(buf))) > 0){write(fd2, buf, count);}close(fd1);//1024close(fd2);//1024return 0;
}
文件定位操作
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
函数功能:文件定位
函数参数:fd:文件描述符offset:相对于偏移量偏移量,每一读写操作所需要移动的距离,单位是字节的数量,可正可负(向前移,向后移)whence: 基准点SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小。
SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量。
SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小。返回值:失败 :-1成功:当前文件的偏移量(当前读写位置相对文件开始位置的字节数)通过lseek计算文件的大小
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd;char buf[1024] = "\0";char writebuf[1024] = "xxxxx";int count;int offset;fd = open("test.txt", O_RDWR | O_CREAT , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);if(-1 == fd){perror("open failed");}count = write(fd, writebuf, strlen(writebuf));if(-1 == count){perror("write failed");return -1;}printf("count = %d\n", count);//offset = lseek(fd, -6, SEEK_CUR);offset = lseek(fd, -5, SEEK_CUR);if(-1 == offset){perror("lseek");return -1;}count = read(fd, buf, 5);if(-1 == count){perror("read failed");return -1;}printf("count = %d : %s\n", count, buf);close(fd);//1024return 0;
}
新建文件,写入“xxxxx”,然后读取出来。

如果向前偏移量越界,时候会直接报错

练习:通过lseek计算文件的大小
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd;char buf[1024] = "\0";char writebuf[1024] = "xxxxx";int count;int offset;fd = open("test.txt", O_RDWR | O_CREAT , S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);if(-1 == fd){perror("open failed");}//SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量0的大小offset = lseek(fd, 0, SEEK_END);if(-1 == offset){perror("lseek");return -1;}printf("sizeof file is %d \n", offset);close(fd);//1024return 0;
}
空洞与非空洞文件 (Linux)
空洞文件和真实文件的区别在于磁盘的使用情况
du -sh 查看空洞文件和真实文件的区别
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd;char buf[32] = "hello, world";int offset;fd = open("hole.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if(fd < 0){printf("%s\n",sterror(errno));return -1;}write(fd, buf, strlen(buf));if((offset = lseek(fd, 168898, SEEK_CUR)) < 0){perror("lseek");return -1;}write(fd, buf, strlen(buf));close(fd);//1024return 0;
}


前后都有hello world.中间全是空洞
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd;char buf[32] = "hello, world";int offset;fd = open("hole.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);if(fd < 0){printf("%s\n",sterror(errno));return -1;}write(fd, buf, strlen(buf));/*if((offset = lseek(fd, 168898, SEEK_CUR)) < 0){perror("lseek");return -1;}
*/
//真实地往磁盘里写内容for(i = 0; i < 168898; i++){write(fd, "x", 1);}write(fd, buf, strlen(buf));close(fd);//1024return 0;
}
空洞和非空洞文件都是165K
但是可以通过查看磁盘使用情况区别两个文件的内存大小

作业:打开file1,file2,file3 ,将file1前一半内容拷贝到file2,后一半内容拷贝file3```cpp
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>int main(int argc, const char *argv[])
{int fd1, fd2, fd3, count, nread, size;int buf[1024] = "\0";if(argc < 4){printf("argc < 4");return -1;}fd1 = open(argv[1], O_RDONLY);if(fd1 < 0){printf("%s\n",sterror(errno));return -1;}fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd2 < 0){printf("%s\n",sterror(errno));return -1;}fd3 = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd3 < 0){printf("%s\n",sterror(errno));return -1;}size = lseek(fd1, 0, SEEK_END);lseek(fd1, 0, SEEK_SET)while((nread = read(fd1, buf, 1)) > 0){count++;if(count <= (size/2))write(fd2, buf, nread);else if (count > (size/2))write(fd3, buf, nread);} close(fd1);//1024close(fd2);//1024close(fd3);//1024return 0;
}
标准IO
只要装了C库,就能使用标准IO
open 返回 fd
fopen 返回指向FILE结构的指针
1)理解什么是流?
输入流入和流出的对象就是流
2)如何描述一个流
内存中开辟一个区域,用来存放流的有关信息,这些信息是保存在一个结构体类型的变量中,该结构体类型是由系统定义的,取名为FILE.
3)FILE 指针
fopen 返回指向FILE结构的指针
流的分类:
文本流和二进制流
(Linux不区分文本和二进制流, windows)
#include <stdio.h>int main(int argc, const char *argv[])
{int i = 0;for(i = 0;i < 379;i++) //每次向缓冲区写3个字符{if (i >= 100)fprintf(stdout,"%d",i);else if (i >= 10)fprintf(stdout,"0%d",i);else if (i >= 0)fprintf(stdout,"00%d",i);}while(1);return 0;}
目标是打印000-376,
实际上只输出前1024字节,
1024/3=341.3 个数,
即000-340.3,因此结尾的4个数是340|3

getchar(); /* why do we need this? 实际使用时才会申请空间*/缓冲:缓冲区是通过malloc申请的空间申请的实际,是发生I/O操作的时候
当程序调用getchar时,程序就等着用户按键,用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中),当用户键入回车之后,getchar才开始从stdio流中每次读入一个字符。
getchar函数的返回值是用户输入的字符的ASCII码,如出错返回-1,且将用户输入的字符回显到屏幕.
总结一下:对于磁盘文件通常使用全缓冲区访问。当流涉及一个终端时(例如标准输入和标准输出),使用行缓冲区标准出错流stderr 通常是不带缓冲区的
#include <stdio.h>
/*1.測試標準輸入和標準輸出採用的是什麼類型的緩衝以及緩衝區的大小*2.普通文件採用的是什麼類型的緩衝以及緩衝區的大小*3.malloc的時間* */
int stream_attribute(FILE *fp)
{if(fp->_flags & _IO_UNBUFFERED) {printf("The IO type is unbuffered\n");} else if (fp->_flags & _IO_LINE_BUF) {printf("The IO type is line buf\n");} else {printf("The IO type is full buf\n");}printf("The IO buffer size = %d (bytes)\n", fp->_IO_buf_end - fp->_IO_buf_base);return 0;
}int main()
{FILE *fp;// 尝试注释掉getchar();观察输出有什么不同getchar(); /* why do we need this? 实际使用时才会申请空间*/printf("stdin ___________________________________\n"); stream_attribute(stdin);printf("stdout ___________________________________\n");stream_attribute(stdout);printf("stderr ___________________________________\n");stream_attribute(stderr);printf("normal file ___________________________________\n");//普通文件if ((fp = fopen("test.txt","w+")) == NULL) {perror("fail to fopen");}printf("before write:\n");stream_attribute(fp);fputc('a', fp); //putchar('a') == fputc('a', stdout)printf("after write:\n");stream_attribute(fp);return 0;
}

文件读操作fopen
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
path : 路径名
mode : 打开方式
r
r+
w
w+
a
a+返回值:成功返回 指向FILE的结构体指针(流指针)失败返回 NULL./hello > fileFILE *freopen(const char *path, const char *mode, FILE *stream);作用:重定向输入输出流const char *restrict pathname:重新定向的文件或者是路径const char *restrict type:文件打开的方式restrict fp
被改变的流返回一个新的FILE指针
freopen("file1", "w+", stdout);
读写流
fgetc和getc基本一致,除了getc()可能是由宏实现的,getchar () == getc(stdin) 。
#include <stdio.h>
int getc(FILE *stream);
int fgetc(FILE *stream);
int getchar(void);
函数参数:stream : 流
从指定流中获取一个字符函数返回值:成功 返回读到字符的ascII码失败 EOF
到文件末尾getchar()等同于getc(stdin);
#include <stdio.h>
int putc(int c, FILE *stream);
int fputc(int c, FILE *stream);
int putchar(int c);
注意:三个函数在使用的时候要一一对应c : 输出的字符(acII码)stream : 指定的流putchar(c)等价于putc(c,stdout)
练习: 循环的从标准输入中获取字符(如果是数字则显示,其他的就不显示)
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>int main(int argc, const char *argv[])
{int c;while(1){c = fgetc(stdin);if( ( c >= '0' ) && ( c <= '9') )fputc(c, stdout);else if('\n' == c)break;}return 0;
}
练习:要求输入字符,将大写字母转换成小写,小写字母转换成大写。
#include <stdio.h>int main(int argc, const char *argv[])
{int c;while(1){c = fgetc(stdin); //放到了缓冲区if((c >= 'a') && (c <= 'z')){c = c - ('a' - 'A');fputc(c, stdout);}else if((c >= 'A') && (c <= 'Z')){c = c + ('a' - 'A');fputc(c, stdout);}if('\n' == c)break;}return 0;
}
行IO
char * fgets(char *s, int size, FILE *stream);
char *gets(char *s);
char* s:用于存储从制定流的缓冲区中获取的数据的首地址
size:缓冲区的长度
stream:指定的流
fgets
(1) 必须指定缓存的长度n。
此函数一直读到下一个新行符为止,但是不超过n-1个字符,读入的字符被送入缓存。该缓存以null字符结尾。
(2) 如若该行,包括最后一个新行符的字符数超过n-1,则只返回一个不完整的行,而且缓存总是以null字符结尾。对fgets()的下一次调用会继续读该行。
缓存:buf[7]

(3) gets()与fgets()的另一个区别是,gets()并不将换行符存入缓存中,fges会将换行符存到存到缓冲区中
#include <stdio.h>int main(int argc, const char *argv[])
{char buf[32];gets(buf);
// fgets(buf, sizeof(buf), stdin);printf("%s", buf);return 0;
}

注意1:char *fgets(char *s ,int size,FILE *stream)的用法
fgets每次至多多size-1个字符,每次都把都到的字符存储到s字符串数组内,当读到文件末尾(EOF)或者是换行的时候它会结束。换行符(‘/n’),会被存储到字符数组内部,结束时会在字符数组最后一位补一个空位(’/0’)。如果文件中的该行,不足bufsize个字符,则读完该行就结束。如若该行(包括最后一个换行符)的字符数超过bufsize-1,则fgets只返回一个不完整的行,但是,缓冲区总是以NULL字符结尾,对fgets的下一次调用会继续读该行。如果n大于一行的字符串长度,那么当读到字符串末尾的换行符时,fgets(..)会返回。并且在s的最后插入字符串结束标志'\0'。 而s缓冲区剩余的位置不会再填充。
注意2:char *gets(char *s,FILE *stream)
gets不推荐使用,该函数容易造成缓冲区的溢出,造成不良的后果。二进制直接I/O。fread()和fwrite()函数支持这种类型的I/O。每次I/O操作读或写某种数量的对象,而每个对象具有指定的长度。这两个函数常用于从二进制文件中读或写一个结构。size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
练习:获取以个文件的行数。
#include <stdio.h>
#include <string.h>int main(int argc, const char *argv[])
{FILE *fp;char buf[1024] = "\0";int line = 0;//step 1: 打开流fp = fopen(argv[1], "r");if(NULL == fp){perror("fopen");return -1;}//step 2: 获取行号while(fgets(buf, sizeof(buf), fp) != NULL){if(buf[strlen(buf) - 1] == '\n')line++;}printf("The line of %s is %d\n", argv[1], line);return 0;
}
int puts(const char *s);
函数功能:向标准输出输出一行
int fputs(const char *s, FILE *stream);
函数功能:向指定的流里输出一行
函数fputs()将一个以null符终止的字符串写到指定的流,终止符null不写出。
== 注意,这并不一定是每次输出一行,因为它并不要求在null符之前一定是新行符。==
#include <stdio.h>int main(int argc, const char *argv[])
{// 中间添加'\0'char buf[] = {'a', 'b', 'c','\0', 'd' , 'e', 'f', '\n', '\0'};fputs(buf, stdout);puts(buf);return 0;
}

puts()将一个以null符终止的字符串写到标准输出,终止符不写出。但是,puts()然后又将一个新行符写到标准输出。
#include <stdio.h>int main(int argc, const char *argv[])
{// 中间添加'\0'char buf[] = {'a', 'b', 'c', 'd' , 'e', 'f', '\n', '\0'};//fputs(buf, stdout);puts(buf);return 0;
}
注意:
puts()然后又将一个新行符写到标准输出。fputs不加换行符。
检查文件出错函数
#include <stdio.h>
void clearerr(FILE *stream);
int feof(FILE *stream);
int ferror(FILE *stream);
函数描述
feof : 用于检测文件末尾标志,如果该标志被设置返回非0的值,如果没有被设置返回0ferror :用于检测出错标志,如果该标志被设置返回非0的值,如果没有设置返回0clearerr :用于清除这两个标志
用法:
while (!feof(fp) && !ferror(stdin)) {cTemp = fgetc(fp);}
直接IO
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
函数参数:ptr: 缓冲区的首地址stream : 流nmemb :元素个数(人为指定的)size : 每个元素的大小(人为指定)返回值:成功 实际读到元素的个数(凑不足一个元素就不算)失败 -1
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
函数参数:ptr : 缓冲区首地址size : 元素的daxiaonmemb :元素的个数stream : 流返回值:成功 实际写入的元素的个数失败 -1
#include <stdio.h>
#include <string.h>
#include <errno.h>int main(int argc, const char *argv[])
{FILE *fp;int data[10];int data1[10];int i;for(i = 0; i < 10; i++){data[i] = i;}if((fp = fopen("test_file", "w+")) == NULL){fprintf(stderr, "fopen failed : %s", strerror(errno));return -1;}fprintf(stdout, "[DEBUG] sizeof(int) = %d\n", sizeof(int));// fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);// data为缓冲区首地址// fwrite把缓冲区的写入fpif(fwrite(data, sizeof(int), 3, fp) != 3){perror("fwrite");return -1;}fseek(fp, 0, SEEK_SET);// 换数组data1// fread把fp的写入缓冲区data1if(fread(data1, 3, 2, fp) != 2){perror("fread");return -1;}// 输出缓冲区for(i = 0; i < 3; i++){printf("%d ", data1[i]);}fclose(fp);return 0;
}
凑不够字节的时候
#include <stdio.h>
#include <string.h>
#include <errno.h>int main(int argc, const char *argv[])
{FILE *fp;char buf[20] = "\0";int nread;if((fp = fopen("test_file", "r")) == NULL){fprintf(stderr, "fopen failed : %s", strerror(errno));return -1;}// nread读取2个元素,每个元素3个字节,所以需要读取6个字节if((nread = fread(buf, 3, 2, fp)) == -1){perror("fread");return -1;}printf("%s\n", buf);printf("nread = %d\n", nread);fclose(fp);return 0;
}
"test_file"里只有1 2 3 4 \0, 5个字节
nread读取2个元素,每个元素3个字节,所以需要读取6个字节,
但是因为"test_file"只有5个字节,所以只能成功读取1个元素,nread==1,凑不够就不算。

流的格式化输入输出
int printf(const char *format, …); //输出到stdout
int fprintf(FILE *stream, const char *format, …); //输出到stream
int sprintf(char *str, const char *format, …); // 输出到char *str
#include <stdio.h>int main(int argc, const char *argv[])
{FILE *fp = NULL;int a = 10;char buf[32] = "hello world";fp = fopen("test", "w+");if(NULL == fp){perror("fopen_2");return -1;}fprintf(fp, "%d %s\n",a, buf);printf("%s\n", buf);// 缓冲区被覆盖sprintf(buf, "hello farsight %d", a);printf("%s\n", buf);a = 11;fprintf(fp, "%d %s\n",a, buf); fclose(fp);return 0;
}

#include <stdio.h>
int scanf(const char *format, …); //stdinint fscanf(FILE *stream, const char *format, …);//stream
#include <stdio.h>int main(int argc, const char* argv[])
{FILE* fp = NULL;int a = 10;char buff[32] = "\0";fp = fopen("test", "r+");if (NULL == fp){perror("fopen");return -1;}fscanf(fp,"%s%d", buff, &a);printf("%s, %d\n", buff, a);fclose(fp);return 0;
}
>
int sscanf(const char *str, const char format, …);//charstr
#include <stdio.h>int main(int argc, const char* argv[])
{FILE* fp = NULL;int a, b;char buff[32] = "\0";char buf[32] = "1 2 hello";fp = fopen("test", "r+");if (NULL == fp){perror("fopen_2");return -1;}sscanf(buf, "%d%d%s", &a, &b, buff);printf("%s, %d, %d\n", buff, a, b);fclose(fp);return 0;
}

流的定位
int fseek(FILE *stream, long offset, int whence);
off_t lseek(int fd, off_t offset, int whence);
参数说明stream : 打开的流offset : 相对于基准点偏移量whence : 基准点返回值: 返回值:偏移量成功 返回0失败 返回-1
返回当前流的偏移量
long ftell(FILE *stream);
#include <stdio.h>int main(int argc, const char *argv[])
{FILE *fp;int ret;long int size;fp = fopen(argv[1], "r+");if(NULL == fp){perror("fopen");return -1;}ret = fseek(fp, 0, SEEK_END);if(-1 == ret){perror("fseek");}size = ftell(fp);printf("sizeo file is %ld\n", size);return 0;
}
void rewind(FILE *stream);
rewind() 等价于 (void)fseek(stream, 0L, SEEK_SET)
实验三:
- 打开文件方式(a+)
- 接上一次的行号(计算行号)
获取时间
time
#include <time.h>
time_t time(time_t *t); time_t == long
函数功能:返回从1970-1-1 0:0:0到函数调用那一刻的秒数如果t非NULL,就将时间填入到t所指向的内存空间返回值:成功 : 时间失败 :((time_t) -1)
#include <time.h>
struct tm *localtime(const time_t *timep);
函数功能:将秒数转换成具体的时间函数参数:timep : 存秒数的地址返回值:
#include <stdio.h>
#include <time.h>int main(int argc, const char *argv[])
{time_t mytime;struct tm *p;mytime = time(NULL);p = localtime(&mytime);printf("%d-%d-%d %d:%d:%d\n", p->tm_year+1900, p->tm_mon+1, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);return 0;
}
文件信息
获取文件属性:
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
函数参数:
struct stat {dev_t st_dev; /* ID of device containing file */ino_t st_ino; /* inode number */mode_t st_mode; /* protection */nlink_t st_nlink; /* number of hard links */uid_t st_uid; /* user ID of owner */gid_t st_gid; /* group ID of owner */dev_t st_rdev; /* device ID (if special file) */off_t st_size; /* total size, in bytes */blksize_t st_blksize; /* blocksize for file system I/O */blkcnt_t st_blocks; /* number of 512B blocks allocated */time_t st_atime; /* time of last access */time_t st_mtime; /* time of last modification */time_t st_ctime; /* time of last status change */ };
三个函数的返回:若成功则为0,若出错则为-1,并且设置errno.给定一个pathname的情况下:stat函数返回一个与此命名文件有关的信息结构fstat函数获得已在描述符filedes上打开的文件的有关信息lstat函数类似于stat,但是当命名的文件是一个符号连接时,lstat返回该符号连接的有关信息,而不是由该符号连接引用的文件的信息stat与之相反,返回的是链接文件引用的文件的信息
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int main(int argc, const char *argv[])
{struct stat buf;stat(argv[1], &buf); printf("size of %s is %d\n", argv[1], buf.st_size);return 0;
}
st_mode
首先S_IFMT是一个掩码,它的值是017000(注意这里用的是八进制), 可以用来过滤出前四位表示的文件类型。其后的连续七个分别对应套接口文件、符号链接文件、普通文件、块设备、目录、字符设备、管道,它们分别对应一个不同的值。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int main(int argc, const char *argv[])
{struct stat buf;lstat(argv[1], &buf);
#if 0if(S_ISREG(buf.st_mode))printf("regular file\n");else if(S_ISDIR(buf.st_mode))printf("directory\n");else if(S_ISCHR(buf.st_mode))printf("character device\n");else if(S_ISBLK(buf.st_mode))printf("block device\n");else if(S_ISFIFO(buf.st_mode))printf("FIFO\n");else if(S_ISLNK(buf.st_mode))printf("symbolic link\n");else if(S_ISSOCK(buf.st_mode))printf("socket\n");
#elseswitch(S_IFMT & buf.st_mode){case S_IFSOCK:printf("socket\n");break;case S_IFLNK:printf("symbolic link\n");break;case S_IFREG:printf("regular file\n");break;case S_IFBLK:printf("block device\n");break;case S_IFDIR:printf("directory\n");break;case S_IFCHR:printf("character device\n");break;case S_IFIFO:printf("FIFO\n");break;default:printf("error\n");}#endifreturn 0;
}

硬链接和软连接
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>int main(int argc, const char *argv[])
{struct stat buf;stat(argv[1], &buf); printf("number of hand link is %d\n", buf.st_nlink);return 0;
}

获取用户相关信息
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
uid : 用户ID
struct passwd {char *pw_name; /* username */char *pw_passwd; /* user password */uid_t pw_uid; /* user ID */gid_t pw_gid; /* group ID */char *pw_gecos; /* user information */char *pw_dir; /* home directory */char *pw_shell; /* shell program */
};
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>int main(int argc, const char *argv[])
{struct stat buf;struct passwd *p;stat(argv[1], &buf); p = getpwuid(buf.st_uid);printf("file owner is %s\n", p->pw_name);return 0;
}

获取用户分组
#include <sys/types.h>
#include <grp.h>
struct group *getgrgid(gid_t gid);
gid :用户组ID
struct group {char *gr_name; /* group name */char *gr_passwd; /* group password */gid_t gr_gid; /* group ID */char **gr_mem; /* group members */
};
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>int main(int argc, const char *argv[])
{struct stat buf;struct group *p;stat(argv[1], &buf); p = getgrgid(buf.st_gid);printf("file group is %s\n", p->gr_name);return 0;
}

目录操作函数
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>int main(int argc, const char *argv[])
{DIR *dir;struct dirent *p;//step 1:打开目录dir = opendir(argv[1]); if(NULL == dir){perror("opendir");return -1;}//step 2:读目录while((p = readdir(dir)) != NULL){if(!strncmp(".", p->d_name, 1))continue;printf("%s\t", p->d_name);}printf("\n");//step 3:关闭目录closedir(dir);return 0;
}
库
Linux的库文件保存在 /lib /usr/lib文件夹下
Linux和window下库文件的格式不兼容。
静态库特点
编译(链接)时把静态库中相关代码复制到可执行文件中,直接加载静态库中对应的代码 (不是全部,是部分)。
程序中已包含代码,运行时不再需要静态库。
静态库升级后,程序需要重新编译链接。
编写库源码hello.c
#include <stdio.h>
void hello(void) {printf(“hello world\n”);return;
}
编译生成目标文件
gcc -c hello.c -Wall
创建静态库 hello
ar crs libhello.a hello.o
静态库名称:libhello.a
名称都是有规范的:libXXXX.a
根据后面的执行文件hello.o生成静态库
查看库中符号信息
nm libhello.a

链接静态库
编写应用程序test.c
#include <stdio.h>
void hello(void);int main() {hello();
return 0;
}
编译test.c 并链接静态库libhello.a
gcc -o test test.c -L. -lhello
因为静态库里的代码已经加载到了程序里,所以删除静态库,不影响程序执行

共享库的特点
编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码。
程序不包含库中代码,尺寸小。
程序运行时需要加载库。
库升级方便,无需重新编译程序。
编写库源码hello.c bye.c
#include <stdio.h>void hello(void) {printf(“hello world\n”);return;
}
#include <stdio.h>void hello(void) {printf(“Bye\n”);return;
}
编译生成目标文件
gcc -c -fPIC hello.c bye.c -Wall
使用相对寻址,可以加载到不同位置
创建共享库 common
gcc -shared -o libcommon.so.1 hello.o bye.o
libcommon.so.1:libcommon.so.X的X代表库的版本,libcommon.so.2,libcommon.so.3…

为共享库文件创建链接文件
ln -s libcommon.so.1 libcommon.so
符号链接文件命名规则
lib<库名>.so
链接共享库
编写应用程序test.c
#include <stdio.h>
#include “common.h”int main() {hello();bye();return 0;
}
编译test.c 并链接共享库libcommon.so
gcc -o test test.c -L. -lcommon
gcc首先链接共享库,再链接静态库
gcc -o test test.c -L. -lcommon -static
指定链接静态库
执行程序
./test
./test: error while loading shared libraries: libcommon.so
cannot open shared object file : No such file or directory
当前目录不在系统搜索路径里,所以会报错。
添加共享库的加载路径
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
LD_LIBRARY_PATH可以添加环境中缺省的路径

如何找到共享库
-
把库拷贝到/usr/lib和/lib目录下
不推荐,会跟系统库混乱 -
在LD_LIBRARY_PATH环境变量中添加库所在路径
临时的,只对当前shell起作用 -
添加/etc/ld.so.conf.d/XXX.conf文件,执行ldconfig刷新



相关文章:
2023-07-29:华清远见嵌入式2017年线下班:文件IO笔记
这里写目录标题 华清远见嵌入式2017年线下班:文件IO笔记文件权限文件IO文件创建和打开操作文件关闭操作出错处理创建设备文件 || create || 老师自己忘了文件读操作练习:计算文件的大小?文件写操作练习:打开file1和file2ÿ…...
2023年第四届“华数杯”数学建模思路 - 复盘:光照强度计算的优化模型
文章目录 0 赛题思路1 问题要求2 假设约定3 符号约定4 建立模型5 模型求解6 实现代码 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 问题要求 现在已知一个教室长为15米,宽为12米࿰…...
Typescript第七章 处理错误(返回null,抛出异常,返回异常,Option类型)
第七章 处理错误 Typescript竭尽所能,把运行时异常转移到编译时。Typescript是功能丰富的系统,加上强大的静态和符号分析能力,包揽了大量辛苦的工作。 但是有些问题是无法避免的,比如网络和文件系统异常,解析用户输入…...
Qt库xcb问题
首先在~/.bashrc中加入 export QT_DEBUG_PLUGINS1然后看具体的报错 查看某个库链接的库: ldd libqxcb.so然后找到真正缺少的库,再在路径下搜索,然后建立软链接。 https://blog.csdn.net/LOVEmy134611/article/details/107212845 https://…...
C++ | 哈希表的实现与unordered_set/unordered_map的封装
目录 前言 一、哈希 1、哈希的概念 2、哈希函数 (1)直接定址法 (2)除留余数法 (3)平方取中法(了解) (4)随机数法(了解) 3、哈…...
【漏洞挖掘】Xray+rad自动化批量漏洞挖掘
文章目录 前言一、挖掘方法二、使用步骤工具安装使用方法开始挖掘 总结 前言 自动化漏洞挖掘是指利用计算机程序和工具来扫描、分析和检测应用程序、网络和系统中的安全漏洞的过程。这种方法可以帮助安全专家和研究人员更高效地发现和修复潜在的安全威胁,从而提高整…...
Swagger UI教程 API 文档和Node的使用
在团队开发中,一个好的 API 文档可以减少很多交流成本,也可以使一个新人快速上手业务。 前言 swagger ui是一个API在线文档生成和测试的利器,目前发现最好用的。为什么好用?Demo 传送门 支持API自动生成同步的在线文档 这些文档可…...
P5691 [NOI2001] 方程的解数
[NOI2001] 方程的解数 题目描述 已知一个 n n n 元高次方程: ∑ i 1 n k i x i p i 0 \sum\limits_{i1}^n k_ix_i^{p_i} 0 i1∑nkixipi0 其中: x 1 , x 2 , … , x n x_1, x_2, \dots ,x_n x1,x2,…,xn 是未知数, k 1 ,…...
rust里用什么表示字节类型?
在Rust中,字节可以使用 u8 类型来表示。 u8 是一个无符号8位整数类型,可以表示0到255之间的值,对应于一个字节的范围。 以下是一个示例,演示了如何声明和使用字节: fn main() {let byte: u8 65; // 表示字母A的ASCI…...
CMake简介
文章目录 为什么需要头文件为什么 C 需要声明头文件 - 批量插入几行代码的硬核方式头文件进阶 - 递归地使用头文件 CMake什么是编译器多文件编译与链接CMake 的命令行调用为什么需要库(library)CMake 中的静态库与动态库CMake 中的子模块子模块的头文件如…...
[threejs]相机与坐标
搞清相机和坐标的关系在threejs初期很重要,否则有可能会出现写了代码,运行时一片漆黑的现象,这种情况就有可能是因为你相机没弄对。 先来看一下threejs中的坐标(世界坐标) 坐标轴好理解,大家只需要知道在three中不同颜色代表的轴…...
Qt信号与槽机制的基石-MOC详解
引入 上篇讲到了信号与槽就是实现的观察者模式,那具体如何生成映射表就是moc做的事情。 一、moc简介 1. moc的定义 moc 全称是 Meta-Object Compiler,也就是“元对象编译器”,它主要用于处理C源文件中的非标准C代码。Qt 程序在交由标准编…...
关于单体架构缓存刷新实现方案
背景 如果各位看官是分布式项目应该都采用分布式缓存了,例如redis等,分布式缓存不在本次讨论范围哈;我个人建议是,如果是用户量比较大,建议采用分布式缓存机制,后期可以很容易前后到分布式服务或微服务。 …...
洞悉安全现状,建设网络安全防护新体系
一、“网络攻防演练行动“介绍 国家在2016年发布《网络安全法》,出台网络安全攻防演练相关规定:关键信息基础设施的运营者应“制定网络安全事件应急预案,并定期进行演练”。同年“实战化网络攻防演练行动”成为惯例。由公安部牵头࿰…...
spring中怎么通过静态工厂和动态工厂获取对象以及怎么通过 FactoryBean 获取对象
😀前言 本章是spring基于XML 配置bean系类中第4篇讲解spring中怎么通过静态工厂和动态工厂获取对象以及怎么通过 FactoryBean 获取对象 🏠个人主页:尘觉主页 🧑个人简介:大家好,我是尘觉,希望…...
三元组表实现矩阵相加(数据结构)
代码: 含注释,供参考 #include <stdio.h> #include <stdlib.h>typedef struct {int row,col,value;//分别为行数,列数,数值 } Triple; typedef struct {int len;//非零数值的个数Triple data[200]; } TSMatrix;void…...
ChinaJoy 2023微星雷鸟17游戏本震撼发布:搭载AMD锐龙9 7945HX首发8499元
ChinaJoy 2023展会中微星笔记本再次给大家带来惊喜,发布了搭载AMD移动端16大核的旗舰游戏本:雷鸟17,更重要的这样一款旗舰性能的游戏本,首发价8499元堪称当今游戏本市场中的“性价比爆款”! 本着和玩家一同制霸游戏战场…...
各种运算符
算术运算符 1.双目运算符 */%:从左到右优先级依次降低 一些注意事项: 1若a/b都为整型那么结果也为整型,如果ab其中有一个为实型,结果则为实型 求余运算符注意事项: 1运算对象必须为整数 2运算结果的整数跟左边数字的…...
yolov3-tiny原理解析及代码分析
前言 从去年十一月份开始学习yolo神经网络用于目标识别的硬件实现,到现在已经六个月了。一个硬件工程师,C/C基础都差劲的很,对照着darknet作者的源码和网上东拼西凑的原理讲解,一点一点地摸索。刚开始进度很慢,每天都…...
深入了解Redis-实战篇-短信登录
深入了解Redis-实战篇-短信登录 一、故事背景二、知识点主要构成2.1、短信登录2.1.1、生成随机短信验证码引入maven依赖生成验证码 2.1.2、实现登录校验拦截器2.1.3、基于Redis实现短信登录2.1.3.1、发送验证码时存入Redis2.1.3.2、登录时校验验证码 2.1.4、解决状态登录刷新的…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

链接共享库