C/C++编程:标准库 - stdio.h 学习

1059 篇文章 286 订阅

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)

实践

  1. getc
#include<stdio.h>

int main()
{
   char c;

   printf("请输入字符:");
   c = getc(stdin);
   printf("输入的字符:");
   putc(c, stdout);
   
   return(0);
}

在这里插入图片描述

  1. 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);
}

参见

  1. 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() 没有存储换行符,所以就不会换行了。

实践

  1. 封装 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);
}

在这里插入图片描述

ferrorint ferror(FILE *stream)
clearerrvoid clearerr(FILE *stream)
perrorvoid 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);
 }
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值