C基础之输入输出(一)

在牛客网做C语言题目时,往往第一步卡脖子的地方就是输入问题,比如:

按行读取数值,以0结束;

按一次性输入读取字符串(中间包含空格)

等等问题,发现基础很重要,干脆整理下,以便后面参考。

Makefile

每个C源文件都编译成对应的.o文件和目标程序

# File paths
SRC_DIR := .
BUILD_DIR := .
OBJ_DIR := $(BUILD_DIR)

# Compilation flags
CC := gcc
LD := gcc
CFLAGS := -Wall

# Files to be compiled
SRCS := $(wildcard $(SRC_DIR)/*.c)
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
BUILD := $(OBJS:$(OBJ_DIR)/%.o=$(BUILD_DIR)/%)

# Don't remove *.o files automatically
.SECONDARY: $(OBJS)

all: $(BUILD)

# Compile each *.c file as *.o files
# @mkdir -p $(OBJ_DIR)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
	@echo + CC $<
	@$(CC) $(CFLAGS) -c -o $@ $<

# Link each *.o file as executable files
# @mkdir -p $(BUILD_DIR)
$(BUILD_DIR)/%: $(OBJ_DIR)/%.o
	@echo + LD $@
	@$(LD) $(CFLAGS) -o $@ $<

.PHONY: all clean

clean:
	rm -rvf $(OBJS) $(BUILD)

一、输入和输出

1.1 perror()、exit()函数

man手册:perror()用于输出系统调用的错误信息

头文件#include <stdio.h>

用法void perror(const char *s)

返回值: 无

NAME
       perror - print a system error message

SYNOPSIS
       #include <stdio.h>

       void perror(const char *s);

       #include <errno.h>

       const char * const sys_errlist[];
       int sys_nerr;
       int errno;       /* Not really declared this way; see errno(3) */

exit()函数用于退出程序:

头文件#include <stdlib.h>

用法void exit(int status),其返回给父进程的值是status & 0xFF

返回值:无

比如下面这个代码,打开一个不存在的文件“test.txt",系统error变量会记录错误信息:“No such file or director”。

//cat test_perror.c:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp = NULL;

    fp = fopen("test.txt", "r");
    if(!fp)
    {
        perror("This Error Message");
        exit(-1);
    }
    fclose(fp);
    fp = NULL;
    return 0;
}

执行结果:

jrg@hygon-RG-CS6020ES:test$ ./test_perror
This Error Message: No such file or directory
jrg@hygon-RG-CS6020ES:test$ echo $?
255
jrg@hygon-RG-CS6020ES:test$
1.2 流

stream分为两种:

stream
文本流
二进制流

I/O函数以基本的三种形式处理数据:

下表列出了用于各种I/O的函数或函数家族( 斜体 表示函数家族,它指一组函数中每个都执行相同的基本任务,形式有所不同)

数据类型输入输出描述
单个字符getcharputchar单个字符I/O
文本行gets
scanf
puts
printf
未格式化I/O
格式化I/O
二进制数据freadfwrite二进制I/O

为何会需要I/O函数家族呢,比如 scanf 家族,因为需要满足不同的需求,主要有三个不同需求:

  • 只用于stdin或stdout
  • 随作为参数的流stream使用
  • 使用内存中的字符串而不是流stream
家族名目的可用于所有的流stream只用于stdin和stdout内存中的字符串
getchar单个字符输入fgetc, getcgetchar对指针使用下标引用或间接访问操作从内存获得一个字符
putchar单个字符输出fputc, putcputchar对指针使用下标引用或间接访问操作向内存写入一个字符
gets文本行输入fgetsgets使用strcpy从内存读取文本行
puts文本行输出fputsputs使用strcpy向内存写入文本行
scanf格式化输入fscanfscanfsscanf
printf格式化输出fprintfprintfsprintf
1.2.1 fopen打开流

fopen家族,打开一个文件

     	#include <stdio.h>

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

       FILE *fdopen(int fd, const char *mode);

       FILE *freopen(const char *pathname, const char *mode, FILE *stream);
		试图先关闭stream流,再重新打开pathname指向的文件,一般用于重定向stdin/stdou/stderr等;

mode:

模式含义
rO_RDONLY
wO_WRONLY | O_CREATE | O_TRUNC(若文件存在,则长度被截为0,属性不变)
aO_WRONLY | O_CREATE | O_APPEND
r+O_RDWR
w+O_RDWR | O_CREATE | O_TRUNC
a+O_RDWR | O_CREATE | O_APPEND
1.2.2 fclose关闭流

头文件#include<stdio.h>

用法int fclose(FILE *stream)

返回值:成功返回0,否则返回EOF并置errno

fclose()按理也需要检查执行成功与否的,因为可能在调用fclose之前,steam已经被改掉了或者破坏了,会导致fclose失败

1.3 字符I/O
1.3.1 输入

fetc, getc, getchar, ungetc

NAME
       fgetc, getc, getchar, ungetc - input of characters and strings

SYNOPSIS
       #include <stdio.h>

       int fgetc(FILE *stream);

       int getc(FILE *stream);

       int getchar(void);

       int ungetc(int c, FILE *stream);

getchar从标准输入读取单个字符,一个一个字符读取

#include <stdio.h>

int main(int argc, char **argv)
{
    char ch;

    while((ch = getchar()) != ' ')
    {
        printf("get ch: %c, 0x%02X\n", ch, ch);
    }

    return 0;
}

输出:

jrg@hygon-RG-CS6020ES:test$ ./test_input
1								#输入1加换行,输出了1以及换行符,也就是说getchar读取了换行符
get ch: 1, 0x31
get ch:
, 0x0A
								#只输入换行符
get ch:
, 0x0A
a								#输入字母a以及换行符
get ch: a, 0x61
get ch:
, 0x0A
								#输入了一个空格,程序结束
jrg@hygon-RG-CS6020ES:test$
1.3.2 输出
NAME
       fputc, putc, putchar - output of characters and strings

SYNOPSIS
       #include <stdio.h>

       int fputc(int c, FILE *stream);

       int putc(int c, FILE *stream);

       int putchar(int c);

将上面的程序稍微修改:

jrg@hygon-RG-CS6020ES:test$ cat test_input.c
#include <stdio.h>

int main(int argc, char **argv)
{
    char ch;

    while((ch = getchar()) != ' ')
    {
        putchar(ch);				##加了一行putchar,将字符输出到标准输出
        printf("get ch: %c, 0x%02X\n", ch, ch);
    }

    return 0;
}

jrg@hygon-RG-CS6020ES:test$ ./test_input
abcd								##输入abcd以及换行,getchar按字符单个读取
aget ch: a, 0x61					##putchar会输出字符
bget ch: b, 0x62
cget ch: c, 0x63
dget ch: d, 0x64
									##此处getchar读取到\n,然后putchar又输出\n,所以此处换行了
get ch:
, 0x0A
									##输入空格(' '),程序结束
jrg@hygon-RG-CS6020ES:test$
1.4 未格式化的行I/O

gets和puts常用语操作字符串

1.4.1 gets输入

SYNOPSIS
       #include <stdio.h>

       char *gets(char *s);
       
       char *fgets(char *s, int size, FILE *stream);

gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with a null byte (’\0’).

gets读取以换行符或者EOF为结束标志。

1.4.2 puts输出
SYNOPSIS
       #include <stdio.h>

       int fputs(const char *s, FILE *stream);

		int puts(const char *s);
jrg@hygon-RG-CS6020ES:test$ cat test_gets.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    char s[200] = {0};

    if(gets(s))					##按行读取
    {
        puts(s);				##输出读取的内容
    }
    else
    {
        perror("gets failed");
        exit(-1);
    }

    return 0;
}
jrg@hygon-RG-CS6020ES:test$
jrg@hygon-RG-CS6020ES:test$ ./test_gets
asdfasd asdfasdfsdfs  12 43 asd   234
asdfasd asdfasdfsdfs  12 43 asd   234
jrg@hygon-RG-CS6020ES:test$
1.5 格式化的行I/O
1.5.1 输入scanf

scanf格式码:scanf接收读取值的参数是地址

代码参数含义
cchar *读取和存储单个字符
i
d
int *可选的有符号整数被转换,d把输入的解释为十进制;i根据它的第1个字符决定值的基数
u
o
x
unsigned *u: 十进制数
o: 八进制数
x: 十六进制数,X和x相同
e
f
g
float *浮点值,E和G分别与e和g相同
schar *非空白字符串,字符串后面会自动加上NUL结束符’\0’
pvoid *输入预期为一串字符
1.5.2 输出printf
代码参数含义
cint参数被裁减为unsigned char 类型并作为字符打印
d
i
int参数作为十进制整数打印
u
o
x,X
unsigned intu: 十进制
o:八进制
x:十六进制小写abcdef, X:十六进制大写ABCDEF
e
E
double参数根据指数形式打印,比如,6.023000e23用e打印,6.023000E23用E打印,小数点后面的位数有精度字段决定,缺失值是6
fdouble参照浮点格式打印,缺省精度6
schar *打印字符串
pvoid *指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值