文章目录
get、fget、fgets
理论
/*
* 功能: 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
* 参数: stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要在上面执行操作的流。
* 返回值: 该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF
*/
int getc(FILE *stream)
/*
* 功能: 从指定的流 stream 获取下一个字符(一个无符号字符),并把位置标识符往前移动。
* 参数: stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要在上面执行操作的流。
* 返回值: 该函数以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生读错误,则返回 EOF
*/
int fgetc(FILE *stream)
/*
功能:从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
参数:
str -- 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
n -- 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。
返回值:
如果成功,该函数返回相同的 str 参数。
如果到达文件末尾或者没有读取到任何字符,str 的内容保持不变,并返回一个空指针。
如果发生错误,返回一个空指针。
*/
char *fgets(char *str, int n, FILE *stream)
实践
- getc
#include<stdio.h>
int main()
{
char c;
printf("请输入字符:");
c = getc(stdin);
printf("输入的字符:");
putc(c, stdout);
return(0);
}
- fget
#include <stdio.h>
int main ()
{
FILE *fp;
int c;
int n = 0;
fp = fopen("file.txt","r");
if(fp == NULL)
{
perror("打开文件时发生错误");
return(-1);
}
do
{
c = fgetc(fp);
if( feof(fp) )
{
break ;
}
printf("%c", c);
}while(1);
fclose(fp);
return(0);
}
- fgets
(1) 从标准输入中读取字符输出到标砖输出
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv){
char str[60];
if(fgets(str, 60, stdin) == NULL){
printf("fgets error");
exit(0);
}
puts(str); /* 向标准输出 stdout 写入内容 */
}
(3) 从指定文件中读取一行
#include <stdio.h>
int main()
{
FILE *fp;
char str[60];
/* 打开用于读取的文件 */
fp = fopen("file.txt" , "r");
if(fp == NULL) {
perror("打开文件时发生错误");
return(-1);
}
if( fgets (str, 60, fp)!=NULL ) {
/* 向标准输出 stdout 写入内容 */
puts(str);
}
fclose(fp);
return(0);
}
假设我们有一个文本文件 file.txt,它的内容如下。文件将作为实例中的输入:
We are in 2014
让我们编译并运行上面的程序,这将产生以下结果:
We are in 2014
(3) 封装
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
char *Fgets(char *ptr, int n, FILE *stream){
char *rptr;
if ( (rptr = fgets(ptr, n, stream)) == NULL && ferror(stream)){
printf("fgets error");
exit(0);
}
return (rptr);
}
int main(int argc, char **argv){
char str[60];
/*
*阻塞等待从fp中读取字符到sendline中,限制最多可以读取MAXLINE个字符,
*而且读取读取到的字符时NULL的话,就重新阻塞读取
* 处理完了之后就重新阻塞等待
*/
while (Fgets(str, sizeof(str), stdin) != NULL) {
puts(str); /* 向标准输出 stdout 写入内容 */
}
}
puts、fputs
理论
# include <stdio.h>
/*
* 作用: 把一个字符串写入到标准输出 stdout,直到空字符,但不包括空字符。换行符会被追加到输出中。
* 参数: str -- 这是要被写入的 C 字符串。
* 返回: 如果成功,该函数返回一个非负值,如果发生错误则返回 EOF。
*/
int puts(const char *str)
/*
* 作用:显示字符串
* 参数:
* s 输出的字符串的首地址
* stream 向何种流输出
*/
int fputs(const char *s, FILE *stream);
fputs() 和 puts() 有两个小区别:
- puts() 只能向标准输出流输出,而 fputs() 可以向任何流输出。
- 使用 puts() 时,系统会在自动在其后添加换行符;而使用 fputs() 时,系统不会自动添加换行符。
那么这是不是意味着使用 fputs() 时就要在后面添加一句“printf("\n");”换行呢?看情况!如果输入时使用的是 gets(),那么就要添加 printf 换行;但如果输入时用的是 fgets(),则不需要。
因为使用 gets() 时,gets() 会将回车读取出来并丢弃,所以换行符不会像 scanf 那样被保留在缓冲区,也不会被 gets() 存储;而使用 fgets() 时,换行符会被 fgets() 读出来并存储在字符数组的最后,这样当这个字符数组被输出时换行符就会被输出并自动换行。
但是也有例外,比如使用 fgets() 时指定了读取的长度,如只读取 5 个字符,事实上它只能存储 4 个字符,因为最后还要留一个空间给 ‘\0’,而你却从键盘输入了多于 4 个字符,那么此时“敲”回车后换行符就不会被 fgets() 存储。数据都没有地方存放,哪有地方存放换行符呢!此时因为 fgets() 没有存储换行符,所以就不会换行了。
实践
- 封装 Fputs
void Fputs(const char *ptr, FILE *stream)
{
if (fputs(ptr, stream) == EOF){
printf("fputs error");
exit(0);
}
}
#include <malloc.h>
#include <assert.h>
int main(int argc,char *argv[]){
FILE *fp1 = fopen("test.txt", "w+");
assert(fp1);
int ret = fputs("hello", fp1);
printf("ret: %d\n", ret);
fclose(fp1);
}
feof
/*
* 作用: 测试给定流 stream 的文件结束标识符。
* 参数: stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
* 返回值: 当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
*/
int feof(FILE *stream)
#include <stdio.h>
int main ()
{
FILE *fp;
int c;
fp = fopen("file.txt","r");
if(fp == NULL)
{
perror("打开文件时发生错误");
return(-1);
}
while(1)
{
c = fgetc(fp); // 从fp中读取一个字符,并且将当前字符指向下一个
if( feof(fp) ) // 检测当前字符是否到达了文件尾。 如果到了文件为,返回非0
{
break ;
}
printf("%c", c);
}
fclose(fp);
return(0);
}
现象:打印输入文本的内容,等读完了就结束死循环
#include <cstdio>
#include <errno.h>
static void test_file(void)
{
const char *filename = "test2.txt";
FILE *fp = fopen(filename, "a+"); // 追加方式写入
char buf[1024];
int i;
if (fp == NULL) {
printf("fopen %s error(%s)\n", filename, errno);
return;
}
for (i = 0 ; i < 100; i++) {
if (fputs("hello world!\n", fp) == EOF) {
printf("fputs to %s error(%s)\n", filename, errno);
break;
}
}
printf("write to %s ok\n", filename);
fclose(fp);
fp = fopen(filename, "r");
if (fp == NULL) {
printf("fopen %s error(%s)\n", filename, errno);
return;
}
i = 0;
while (!feof(fp)) {
if (fgets(buf, sizeof(buf), fp) == NULL) {
printf("fgets return null, i=%d\n", i);
} else {
i++;
printf(">>gets line(%d): %s", i, buf);
}
}
fclose(fp);
}
int main() {
test_file();
}
ferror、clearerr
理论
/*
* 作用: 测试给定流 stream 的错误标识符。
* 参数: stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
* 返回值: 当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。
*/
int ferror(FILE *stream)
/*
* 作用: 清除给定流 stream 的文件结束和错误标识符
* 参数: stream -- 这是指向 FILE 对象的指针,该 FILE 对象标识了流。
* 返回值: 这不会失败,且不会设置外部变量 errno,但是如果它检测到它的参数不是一个有效的流,则返回 -1,并设置 errno 为 EBADF
*/
void clearerr(FILE *stream)
实践
#include <stdio.h>
int main()
{
FILE *fp;
char c;
fp = fopen("file.txt", "w"); // 以只写模式打开的文件
c = fgetc(fp); // 从文件指针stream指向的文件中读取一个字符,读取一个字节后,光标位置后移一个字节
if( ferror(fp) ) // 只写文件不可以读,所以一定会出现错误
{
printf("读取文件:file.txt 时发生错误\n"); // 打印
}
clearerr(fp); // 清楚了fp的错误
if( ferror(fp) )
{
printf("读取文件:file.txt 时发生错误\n");// 不打印
}
fclose(fp);
return(0);
}
perror
理论
/*
* 作用: 把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格。
* 参数: str -- 这是 C 字符串,包含了一个自定义消息,将显示在原本的错误消息之前。
*/
void perror(const char *str)
实践
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("aaaa.txt", "r");
if( fp == NULL ) {
perror("Error: ");
return(-1);
}
fclose(fp);
return(0);
}
ferror | int ferror(FILE *stream) |
clearerr | void clearerr(FILE *stream) |
perror | void perror(const char *str) |
fprintf、 snprintf()、fflush()
理论
/*
* 功能: 发送格式化输出到流 stream 中。
* format -- 这是 C 字符串,包含了要被写入到流 stream 中的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是 %[flags][width][.precision][length]specifier
*/
int fprintf(FILE *stream, const char *format, ...)
/*
* 功能: 设将可变参数(...)按照 format 格式化成字符串,并将字符串复制到 str 中,size 为要写入的字符的最大数目,超过 size 会被截断。
* 参数: str -- 目标字符串。
* size -- 拷贝字节数(Bytes)。
* format -- 格式化成字符串。
* ... -- 可变参数。
* 返回值: 实际复制的字符长度
* (1) 如果格式化后的字符串长度小于等于 size,则会把字符串全部复制到 str 中,并给其后添加一个字符串结束符 \0;
* (2) 如果格式化后的字符串长度大于 size,超过 size 的部分会被截断,只将其中的 (size-1) 个字符复制到 str 中,并给其后添加一个字符串结束符 \0,返回值为欲写入的字符串长度。
*/
int snprintf ( char * str, size_t size, const char * format, ... );
/*
* 作用: 刷新流stream的输出缓冲区
* 参数: stream -- 这是指向 FILE 对象的指针,该 FILE 对象指定了一个缓冲流
* 返回值:如果成功,该函数返回零值。如果发生错误,则返回 EOF,且设置错误标识符(即 feof)
*/
int fflush(FILE *stream)
实践
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("file.txt", "w"); // 可写方式打开file.txt(如果不存在则创建)
fprintf(fp, "%s %s %s %d", "We", "are", "in", 2014);
fclose(fp);
return(0);
}
#include <stdio.h>
#include <string.h>
#include <zconf.h>
int main ()
{
char buff[1024];
memset(buff, '\0', sizeof(buff));
fprintf(stdout, "启用全缓冲\n");
setvbuf(stdout, buff, _IOFBF, 1024);
fprintf(stdout, "这里是qqq\t");
fprintf(stdout, "该输出将保存到 buff\n");
fflush(stdout); // 刷新就马上打印
fprintf(stdout, "这将在编程结束时出现\t");
fprintf(stdout, "最后休眠五秒钟\n");
sleep(5);
// 程序结束时会刷新缓冲区
return(0);
}
#include <string.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
char buf[10];
const char* s9 = "xxxxxxxxx";
const char* s10 = "xxxxxxxxxx";
const char* s11 = "xxxxxxxxxxxxxxxxxx";
int ret;
memset(buf, 0, sizeof(buf));
ret = snprintf(buf, sizeof(buf), "%s", s9);
printf("buf size: %d, string len: %d, ret: %d, buf: %s\r\n",
(int) sizeof(buf), (int) strlen(s9), ret, buf);
memset(buf, 0, sizeof(buf));
ret = snprintf(buf, sizeof(buf), "%s", s10);
printf("buf size: %d, string len: %d, ret: %d, buf: %s\r\n",
(int) sizeof(buf), (int) strlen(s10), ret, buf);
memset(buf, 0, sizeof(buf));
ret = snprintf(buf, sizeof(buf), "%s", s11);
printf("buf size: %d, string len: %d, ret: %d, buf: %s\r\n",
(int) sizeof(buf), (int) strlen(s11), ret, buf);
return 0;
}
从上面可以看出:如果超过buf就截断
vprintf()
理论
/*
* 功能:使用参数列表发送格式化输出到标准输出stdout
* format -- 这是字符串,包含了要被写入到标准输出 stdout 的文本。它可以包含嵌入的 format 标签,format 标签可被随后的附加参数中指定的值替换,并按需求进行格式化。format 标签属性是 %[flags][width][.precision][length]specifier
*/
int vprintf(const char *format, va_list arg)
实例
#include <stdio.h>
#include <stdarg.h>
void writeFormat(char *format, ...){
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
int main () {
writeFormat("(%s:%d) test\n", __FUNCTION__ , __LINE__);
writeFormat("(%s:%d) test\n", __FUNCTION__ , __LINE__);
return(0);
}
在生产环境中可用于日志模块
static int __stdout_enable = 1;
void msg_info(char *fmt, ...){
va_list ap;
va_start(ap, fmt);
if(__stdout_enable){
printf("msg_warn->pid(%d), ", getpid());
vprintf(fmt, ap);
printf("\r\n");
}
va_end(ap);
}
int main () {
msg_info("(%s:%d) test", __FUNCTION__ , __LINE__);
msg_info("(%s:%d) test", __FUNCTION__ , __LINE__);
return(0);
}
1、库变量
size_t | 这是无符号整数类型,它是 sizeof 关键字的结果 |
FILE | 这是一个适合存储文件流信息的对象类型。 |
fpos_t | 这是一个适合存储文件中任何位置的对象类型 |
2、库宏
NULL | 这个宏是一个空指针常量的值。 |
EOF | 这个宏是一个表示已经到达文件结束的负整数。 |
3、关于文件权限
int chmod(const char * path, mode_t mode);
函数说明:chmod()会依参数mode 权限来更改参数path 指定文件的权限。
参数 mode 有下列数种组合:
1、S_ISUID 04000 文件的 (set user-id on execution)位
2、S_ISGID 02000 文件的 (set group-id on execution)位
3、S_ISVTX 01000 文件的sticky 位
4、S_IRUSR (S_IREAD) 00400 文件所有者具可读取权限
5、S_IWUSR (S_IWRITE)00200 文件所有者具可写入权限
6、S_IXUSR (S_IEXEC) 00100 文件所有者具可执行权限
7、S_IRGRP 00040 用户组具可读取权限
8、S_IWGRP 00020 用户组具可写入权限
9、S_IXGRP 00010 用户组具可执行权限
10、S_IROTH 00004 其他用户具可读取权限
11、S_IWOTH 00002 其他用户具可写入权限
12、S_IXOTH 00001 其他用户具可执行权限
注:只有该文件的所有者或有效用户识别码为0,才可以修改该文件权限。
基于系统安全,如果欲将数据写入一执行文件,而该执行文件具有S_ISUID 或S_ISGID 权限,则这两个位会被清除。如果一目录具有S_ISUID 位权限,表示在此目录下只有该文件的所有者或root 可以删除该文件。
返回值:权限改变成功返回0, 失败返回-1, 错误原因存于errno.
错误代码:
1、EPERM 进程的有效用户识别码与欲修改权限的文件拥有者不同, 而且也不具root 权限.
2、EACCESS 参数path 所指定的文件无法存取.
3、EROFS 欲写入权限的文件存在于只读文件系统内.
4、EFAULT 参数path 指针超出可存取内存空间.
5、EINVAL 参数mode 不正确
6、ENAMETOOLONG 参数path 太长
7、ENOENT 指定的文件不存在
8、ENOTDIR 参数path 路径并非一目录
9、ENOMEM 核心内存不足
10、ELOOP 参数path 有过多符号连接问题.
11、EIO I/O 存取错误
#include
#include
main()
{
chmod("/etc/passwd", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
}