IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言进阶15-I/O函数 -> 正文阅读

[C++知识库]C语言进阶15-I/O函数

前言:与早期C相比,ANSI C 的一个最大的进步就是它的规范里包含了函数库。每个ANSI编译器必须支持一组特定的函数,并规范所要求的接口,而且按照规定的行为工作。ANSI编译器并未禁止在它们的函数库的基础上增加其他函数。但是,标准库函数必须根据标准所定义的方式执行。如果关心可移植性问题,只要避免使用任何非标准函数就可以。

15.1 错误报告

1、perror函数(定义在 stdio.h中) ** 以一种简单的、统一的方式报告错误。ANSI C函数库的许多函数调用操作系统来完成任务,尤其是输入输出函数。当操作系统执行任务时,都存在失败的可能。标准库函数在一个外部整型变量 errno(在errno.h中定义)中保存错误代码**之后把这个信息传递给用户程序,提示操作失败的原因。

2、perror函数原型:void perror( char const *message );

3、如果message不是null指针并且指向一个非空字符串,perror函数就打印出这个字符串,后面再跟一个分号和空格,再打印出解释 errno 当前错误代码的信息。

4、良好的编程实践要求任何可能差生错误的操作都应该再执行之后检查,即使是十拿九稳的操作,它们迟早可能失败。

15.2 终止执行

1、exit函数(原型定义于 stdlib.h中) 原型为:void exit( int status );

2、status参数返回给操作系统,用于提示程序是否完成。这个值和main函数返回的整型状态值相同

3、预定义符号EXIT_SUCCESS(0),EXIT_FAILURE(1)。分别提示程序的终止是成功还是失败。

4、当程序发生错误无法继续执行下去,通常会在调用 perror函数后面再调用 exit终止函数。

15.3 ANSI I/O概念

头文件stdio.h包含了于ANSI函数库的I/O部分有关的声明。

15.3.1 流(文本流和二进制流)

计算机拥有很多不同的外设,每种设备具有不同的特性和协议。操作系统负责这些不同的设备的通信细节,并向程序员提供了一个更为简单和统一的i/o接口。

ANSI C 进一步对I/O概念抽象,所有的I/O操作都是简单的从程序移进或移出字节,这种字节流被称为流(stream)。程序只需要关心正确的创建输出字节数据,以及正确的解释输入的字节数据。

绝大部分流是完全缓冲的(fully buffered),这意味着读取和写入实际上是从一块称为缓冲区(buffer)的内存区域 来回复制数据。用于输出流的缓冲区只有被写满时才会刷新(flush,物理写入)到外设或文件中。这种方法相较于逐片输出分别写入效率高。与之相对应的输入缓冲区为空时会从外设或者文件读取一块较大的输入,重新填充缓冲区。

15.3.2 文本流(text stream)和二进制流(binary stream)

1、文本流在不同的系统有不同的特征。如文本的最大长度,标准定义最少为254字符。还有像文本行的结束方式,ms-dos系统以

回车’\r’和换行符’\n’结束;再unix系统中只需要换行符’\n’结束。

注:标准规定文本行定义为零个或者若干个字符,后面以一个表示结束的换行符。

2、二进制流的字节将完全根据程序编写他们的形式写入到文件或外设中,完全根据他们从外设和文件读取的形式读入到程序中。

15.3.3 文件

stdio.h包含的声明之一就是FILE结构,注意和磁盘上存储的数据文件区分。**FILE是一个数据结构,用于访问一个流。**如果同时激活几个流,那么没给流都有一个相应的FILE与它相关联。

ANSI C 程序运行时至少提供3个流:标准输入(standard input)、标准输出,标准错误。这些流的名字分别为 stdin, stdout, stderr,它们都是指向FILE结构的指针。

15.3.4 标准I/O常量

EOF常量表示到达了文件尾。

FOPEN_MAX,一个程序至少同时能打开FOPEN_MAX个文件。

FILENAME_MAX 提示一个字符数组有多大,一遍容纳编译器所支持的最长合法文件名。

15.4 流I/O总览

关于文件i/o的一半概况:

1、程序为必须同时初始活动状态的每个文件声明一个指针变量,其类型为:FILE *由流使用。

2、流通过调用fopen函数打开。为打开一个流,必须指定需要访问的文件或设备以及他们的访问方式 (如:读,写,既读又写)。fopen函数向操作系统验证文件或者设备是否存在,以及是否允许指定的访问方式。并初始化FILE结构。

3、fclose函数关闭流。关闭一个流可以防止与它相关联的文件被再次访问,保证每种形式都有一组特定的函数对他们处理。

标准流的I/O并不需要打开或者关闭。

执行字符,文本行,二进制I/O的函数:

数据类型输入输出描述
字符getcharputchar读取或写入单个字符
文本行scanfputs,printfputs 文本行未格式化输出。其余格式化输入输出。
二进制freadfwrite读取或者写入二进制数据

1、只作用于标准输入输出:stdin,stdout

2、随作为参数的流使用

3、使用内存的字符串而不是流。

下表解读方法:一个getchar家族有fgetc,getc, getchar 函数,目的和效果都在一行的表中。

输入输出函数家族:

家族名目的可用于所有流只用于stdin,stdout内存中的字符串
getchar字符输入fgetc, getcgetchar下标引用从内存获取
putchar字符输出fputc, putcputchar下标引用向内存写入
gets文本行输入fgetsgets --已禁用strcpy从内存复制
puts文本行输出fputsputsstrcpy向内存写入
scanf格式化输入fscanfscanfsscanf
printf格式化输出fprinfprintfsprintf

15.5 打开流

fopen函数打开一个特定的文件,并把一个流和这个文件相关联。它的原型如下:

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

两个字符串参数,name是想要打开文件或设备的名字,mode决定流的模型。

常见的mode:

读取写入添加
文本流“r”“w”“a”
二进制流“rb”“wb”“ab”

mode 以r,w,a开头。如果一个打开的文件是用于,它原先的已经存在,它的内容将被删掉。a开头是用于文件的更新,不会删掉原来的内容。数据只能从文件末尾写入。

如果fopen函数打开成功,它将返回一个指向FILE 结构的指针,该结构代表了讲真个新创建的流。函数失败返回NULL指针,errno会提示错误性质。这里就需要检查fopen 的返回值,确保正确打开文件。

例子:

FILE *input
input = fopen( "data", "r" );
if( input == NULL ){
    perror( "data" );
    exit( EXIT_FAILURE );
}

//如果打开失败,perror会返回如下信息
data: No such file or directory

freopen函数用于打开或者重新打开一个特定的文件流。原型如下:

FILE *freopen( char const *filename, char const *mode, FILE *stream );

最后的一个参数就是需要打开的流。可能是之前fopen函数返回的值,也肯能是标准流stdin,stdout,stderr。

15.6 关闭流

流是用函数fclose关闭的,原型如下:

int fclose( FILE *f );

对于输出流,fclose函数在文件关闭之前刷新缓冲区。如果fclose关闭成功,则返回零值,反之返回EOF。

实例:

/*
	处理每个文件名出现于命令行的文件
*/
#include <stdlib.h>
#include <stdio.h>

int main( int ac, char **av )
{
    int exit_status = EXIT_SUCCESS;
    FILe *input;
    
    /*
    	当还有其他文件名时
    */
    while( *++av != NULL ){
		/*
			试图打开这个文件
		*/
        input = fopen( *av, "r" );
        if( input == NULL ){
            perror( *av );
            exit_status = EXIT_FAILURE;
            continue;
        }
        
        /*
        	处理这个文件
        */
        ...
            
        /*
        	关闭这个文件
        */
         if( fclose( input ) != 0){
             perror( "fclose" );
             exit( EXIT_FAILURE);
         }
    }
    return exit_status;
}

对文件关闭是否成功也一般需要测试。

15.7 字符I/O

1、字符输入是 getchar家族执行的,它们的原型如下:

int   fgetc  ( FILE *stream ); //真正的函数
int   getc   ( FILE *stream ); //通过#define定义的宏
int   getchar( void );         //通过#define定义的宏

需要操作的流作为参数传递给 fgetc, getc;getchar始终从标准输入读取。每个函数从流中读取一个字符并返回它的值;如果流中国不存在字符,函数就返回常量值EOF。这些函数返回值为整型是为了允许函数报告文件末尾(EOF);EOF所选择的实际值比一个字符多几位,为了避免二进制的值被错误的解释为EOF。如果返回为一个char型,那么256个字符终必有一个被指定为表示EOF,造成错误。

2、字符输出是putchar家族执行的,它们的原型如下:

int  fputc  ( int character, FILE *stream ); //真正的函数
int  putc   ( int character, FILE *stream ); //通过#define定义的宏
int  putchar( int character );               //通过#define定义的宏

character参数时需要打印的字符。在打印之前函数会裁剪为一个无符号整数型值。向一个已经关闭的文件流写,导致函数失败吗,返回EOF。

15.7.1 撤销字符I/O

在实际读入之前,无法得知下一个字符是什么,因此必须连续读,直到读到与条件不相符的字符,但是这个字符已经读出,又不想丢弃。这时候需要用到函数 ungetc,它的原型如下:

int ungetc( int character, FILE *stream );

ungetc函数 可以把之前读入的字符返回到字符流中,这样下次读可以重新读入。

例子:

/*
	把一串字符从标准中读入并转换为整数。
*/
#include <stdio.h>
#include <ctype.h>

int read_int()
{
    int value = 0;
    int ch;
    /*
    	从标准输入读入数字,得到一个非数字字符时停止。
    */
    while( ch = getchar() ) != EOF && isdigit(ch) ){
        value *= 10;
        value += ch - '0';
    }
    /*
    	把非数字字符退回到流中
    */
    ungetc( ch, stdin );
    return value;
}

注:如果一个流允许退回多个字符,那么这些字符再次被读取的时顺序就以退回时的反序进行。退回字符和流的当前位置有关,如果使用fseek,fsetpos,rewind函数改变了流的位置,退回的字符都将被丢弃。与一个流相关联的外部存储并不受ungetc的影响。

15.8 未格式化的行I/O

1、行I/O可以两种方式执行—未格式化的和格式化的。这两种都用于操作字符串。

2、未格式化的I/O(unformatted line I/O)只是简单的读取或者写入字符串。

3、格式化的I/O 需要执行数字和其它变量在内部和外部表示形式的转换。

gets和puts函数家族用于操作字符串而不是当个字符。它们在处理一行 行文本输入输出程序中非常有用。函数原型如下:

char *fgets( char *buffer, int buffer_size, FILE *stream );
char *gets ( char *buffer ); //现在编译器已经禁用该函数

int   fputs( char const *buffer, FILE *stream );
int   puts ( char const *buffer );

fgets函数: 从指定的stream流读取字符并把它们复制到buffer中,当它读取一个换行符并存储到缓冲区之后就不再读取,如果缓冲区内存储的字符数达到buffer_size-1个时它也会停止读取。在这种情况并不会出现数据丢失的情况,因为下一次调用fgets时将从流的下一字符开始读取。在任何一种情况下,一个NUL字节将被添加到缓冲区所存储的数据的末尾。 如果刚开始就到了文件尾,缓冲区未进行修改,函数返回一个NULL指针;否则返回指向缓冲区的指针。这个返回值通常作为检查是否达到了文件尾。

fputs函数: 传递给fputs缓冲区必须包含一个字符串,它的字符被写入到流中。这个字符串预期以NUL字节结尾,字符串逐字写入:如果没有包括换行符就没有,有多少换行符就输出多少换行符。如果写入有错误,函数返回EOF;否则返回一个非负数。

gets函数和puts函数 几乎和fgets,fputs函数相同。他们之所以存在是为了允许向后兼容。主要区别:gets读入一行时,它并不在缓冲区中存储换行符。当puts写入一个字符串时,它在字符串写入之后再向输出中添加一个换行符

注:gets函数没有定义缓冲区的长度参数如果一个长输入行读到一个短的缓冲区,多出来的字符将写到缓冲区接下来的内存位置,破环后面不相干的值。

实例:

/*
	把标准输入读取到的文本行逐行复制到标准输出。
	不管长行是被一次读取还是分段读取,它所产生的结果文件都是相同的。
	缓冲区的长度也就是读取的一行文本的最大长度。
	fgets无法把字符串读到一个长度小于两个字符的缓冲区,因为一个字符需要为NUL字节保留。
*/
#include <stdio.h>

#define MAX_LINE_LENGTH   1024

void copyline( FILE *input, FILE *outpt )
{
    char buffer[MAX_LINE_LENGTH];
    
    while( fgets( buffer, MAX_LINE_LENGTH, input ) != NULL )
        fputs( buffer, output );
}

15.9 格式化的行I/O

15.9.1 scanf家族

这些函数原型如下,每个原型的省略号表示一个长度可变长度的指针列表。从输入转换来的值逐个存储到这些指针参数所指向的内存位置。

int fscanf( FILE *stream, char const *format, ... );
int scanf ( char const *format, ... );
int sscanf( char const *string, char const *format, ... );

这些函数都是从输入源读取字符并根据format字符串给出的格式码对他们进行转换。fscanf的输入源就是作为参数给出的流,scanf从标准输入读取,而sscanf则从第一个参数所给出的字符串中读取字符。

当格式化字符串到达末尾或者读取的输入不再匹配格式化字符串所指定的类型时,输入就停止。一般情况下,被转换的输入值的数目作为函数的返回值返回。如果任何输入被转换之前文件就已经到达尾部,也就是说没有输入一个值,函数就返回常量值EOF。

注:到这里就知道为什么scanf参数前面为什么要加一个&,由于前面的值参数传递机制,把一个内存位置作为参数作为参数传递给函数的唯一方法就是传递给一个指向该位置的指针。如果忘记&,那么变量的值会被当作地址传给函数,她被解引用时会停止程序或修改这个变量值的地址。

15.9.2 scanf格式代码

scanf函数家族,format字符串参数包含下列内容:

1、空白字符----忽略输入中的空白字符

2、格式代码----指定函数怎么解释接下来的输入字符

3、其他字符----当任何其他字符出现在格式字符串时,下一个输入字符必须与它匹配。如果匹配,该输入的字符随后就被丢弃。如果不匹配,函数直接返回。

scanf家族的格式代码都以一个百分号开头,后面可以是一个可选的星号,一个可选的宽度,一个可选的限定符,格式代码星号将转换后的值被丢弃而不是存储。宽度以一个非负的整数给出,用于限定被读取用于转换的输入字符个数;如果没有给出宽度,函数自动连续读入字符,直到遇见输入中下一个空白字符。限定符用于修改有些格式代码的含义,目的是为了指定参数的长度

scanf格式代码:

代码参数含义
cchar *读取和存储单个字符。前导的空白符并不跳过。如果给出宽度,就读取这个数目的字符。字符后面不会添加NUL字节。参数必须指向一个足够大的数组。
i dint *一个可选的有符号数转换,d把输入解释为十进制数。i根据它第一个字符决定值的基数。
u o xunsigned *一个可选的有符号数转换,但被存储为无符号数。u 值被解释为十进制,o八进制,x十六进制。
e f gfloat *期待一个浮点值,小数点非必须。
schar *读取一串非空白字符。参数指向足够大的数组,当发现空白时输入就停止。字符串后面会自动加上NUL字节。
[xxx]char *根据给定组合的字符从输入中读取一串字符,参数指向一个足够大的数组。当出现第一个不在给定组合的字符时停止输入。字符串后面会自动加上NUL字节。例:%[abc],给定的组合为a,b,c。%[^a,b,c]表示除了abc以外的所有字符。
pvoid *与printf中的%p组合使用
nint *到目前为止从输入读取的字符数目将放回,不消耗任何输入。
%(无)%丢弃

scanf的限定符,使用限定符的结果

格式代码hlL
d i nshortlong
o u xunsigned shortunsignged long
e f gdoublelong double

提示:在整型长度和short相同的机器上,在转换一个short值时,限定符h没有作用。整型比short长时有必要。

实例1:

/*
	用sscanf函数处理行定向(line-oriented)的输入
*/
#include <stdio.h>
#define BUFFER_SIZE    100 //处理的最长行

void function( FILE *input )
{
    int a, b, c, d, e;
    char buffer[ BUFFER_SIZE ];
    
    while( fgets( buffer, BUFFER_SIZE, input ) != NULL ){
        if( sscanf( buffer, "%d %d %d %d %d", &a, &b, &c, &d, &e ) != NULL ){
            fprintf( stderr, "bad input skipped: %s", buffer );
            continue;
        }
    }
    
}

实例2:

/*
	使用sscanf处理可变格式的输入
*/
#include <stdio.h>
#include <stdlib.h>

#define DEFAULT_A 1
#define DEFALUT_B 2

void function ( char *buffer )
{
    int a, b, c;
    
    if( sscanf( buffer, "%d %d %d", &a, &b, &b) != 3 ){
        a = DEFAULT_A;
        if( sscanf( buffer, "%d %d", &b, &c)){
            b = DEFALUT_B;
            if( sscanf( buffer, "%d", &c) != 1){
                fprintf( stderr, "bad input: %s", buffer );
                exit( EXIT_FAILURE );
            }
        }
    }
}

15.9.3 printf家族

printf函数家族用于创建格式化的输出。它们的原型如下:

int fprintf( FILE *stream, char const *format, ... );
int printf ( char const *format, ... );
int sprintf( char *buffer, char const *format, ... );

提示:使用fprintf可以输出任何输出流。而sprintf把它的结果作为一个NUL结尾的字符串存储到指定的buffer缓冲区而不是写到流中 这就容易造成输出结果溢出缓冲区。这三个函数返回值是 实际打印或存储的字符的数目。

printf函数原型中的format字符串可能包含格式代码,格式代码右移个%开头,后面跟着零个或者多个标志字符,用于修改有些转换的执行方式;一个可选的最小字段宽度一个可选的精度一个可选的修改符转换类型

printf格式代码:

代码参数含义
cint参数被裁减为unsigned char 类型并打印字符
i dint参数作为一个十进制数打印。给出精度,位数少于精度,前面填0
u o x Xunsigned int参数作为无符号数打印。u-10 , o-8, x-16
e Edouble参数根据指数形式打印。例如 6.023000e23是使用代码e。小数点后面的位数由精度确定,缺省值为6
fdouble参数按照常规的浮点数打印。小数点后面的位数由精度确定,缺省值为6
g Gdouble参数以%f或%e的形式打印。如果指数大于-4小于精度字段,则用%f格式,否则用指数格式%e
schar *打印一个字符串
pchar *与scanf中的%p组合使用
nint *不产生输出,但目前为止所有函数产生的输出字符数目都将被保存在对应的参数中。
%(无)打印一个 %

printf格式标志:

标志含义
-值在字段中是左对齐,缺省的情况下是右对齐
0
+在正数面前加上‘+’
空格
#

printf格式代码修改符:

修改符用于…表示的参数是
hd i u o x Xshort型整数
hn一个指向short型整数的指针
ld i u o x Xlong型整数
ln指向Long型整数的指针
Le E f g Glong double 型

printf转换的其他形式:

用于#标志
o保证产生一个值以零开头
x X在非零值前面加上 0x
e E f确保结果时钟包含一个小数点,即使没有小数
g G同e E f

15.10 二进制 I/O

把数据写到文件中时,效率最高的是使用二进制形式写入。二进制输出避免了数值转换为字符串的过程中所涉及的开销和精度损失。

fread,fwrite分别用于读取二进制和写入二进制。它们原型如下:

/*
	buffer:一个指向保存数据的内存位置的指针,被解释为一个或多个值的数组。
	size:缓冲区每个元素的字节数
	count:元素数
*/
size_t fread ( void *buffer, size_t size, size_t count, FILE *stream );
size_t fwrite( void *buffer, size_t size, size_t count, FILE *stream );

15.11 刷新和定位函数

fflush函数 ,它迫使一个输出流的缓冲区内的数据立即根据行物理写入,不管它是不是已经写满。原型如下:

int fflush( FILE *stream );

当需要立即把输出缓存区的数据进行物理写入时,应该使用这个函数。 例如:条用fflush可以保证调试信息实时打印出来,而不是保存在缓冲区中直到以后在打印。

ftell函数 和 fseek函数:

在正常情况下,数据以线性方式写入,这意味着在文件中,后面写入的数据的位置实在以前所有写入数据的后面。C同时支持随机访问I/O,也就是以任意的顺序访问文件的不同位置。随机访问是通过读取或写入前定位文件中需要的位置来实现的。ftell函数 和 fseek函数用于执行这项操作。它们的原型如下:

long ftell( FILE *stream );
int  fseek( FILE *stream, long offset, int from );

ftell函数返回流当前的位置,也就是下次开始的位置距离文件起始位置的偏移量。在二进制流中,这个偏移量就是当前文件位置距离起始位置之间的字节数。ftell函数的返回值总是可以用于fseek函数,作为一个距离文件的起始位置的偏移量。

fseek函数允许在一个流中进行定位。这个操作将改变下一个开始的位置。

fseek函数的from参数值:

如果from是…将定位到…
SEEK_SET从流的起始位置起offset个字节,offset非负值
SEEK_CUR从流的当前位置起offset个字节
SEEK_END从流的尾部位置起offset个字节

还有3个额外的函数执行相同的任务,但限制更严格。它们的原型如下:

void rewind ( FILE *stream );
int  fgetpos( FILE *stream, fpos_t *postion );
int  fsetpos( FILE *stream, fpos_t const *postion );

rewind函数将读写指针设置到指定流的起始位置,同时清楚流的错误标志。fgetpos在这个position位置存储文件的当前位置,fsetpos把这个postion位置设置为文件的当前的位置。

注:使用一个从fgetpos返回的fpost_t类型的值是唯一的安全的用法,把它作为参数传递给fsetpos函数。

15.12 改变缓冲方式

在指定的流被打开还没有执行任何操作时,能用下面这俩函数改变缓冲方式。

void setbuf ( FILE *stream, char *buf );
int  setvbuf( FILE *stream, char *buf, int mode, size_t size );

15.13 流错误函数

下面函数用于判断流的状态:

int feof( FILE *stream );      //如果流当前位于文件尾,返回真
int ferror( FILE *stream );    //如果出现任何读写错误,返回真
void clearerr( FILE *stream ); //对指定流的错误标志执行重置。

15.14 临时文件

tmpfile函数使用一个文件来临时保存数据,程序结束时,这个文件就被删除。它的函数原型如下:

FILE *tmpfile( void );

这个函数创建了一个文件,这个文件以wb+模式打开,这使它可以二进制和文本数据。

临时文件的名字可以用tmpnam函数创建,它的原型如下:

char *tmpname( char *name );

15.15 文件操纵函数

两个其他函数,操作文件成功返回0,失败返回非零值,它们的原型如下:

int remove( char const *filename );  //删除一个文件,如果文件正打开,其结果则取决于编译器。
int rename( char const *oldname, char const *newname ); 
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:12:28  更:2022-02-26 11:15:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:04:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码