Skip to content

Latest commit

 

History

History
206 lines (142 loc) · 7.04 KB

9.md

File metadata and controls

206 lines (142 loc) · 7.04 KB

C 编程,第 2 部分:文本输入和输出

原文:Processes, Part 1: Introduction

校验:_stund

自豪地采用谷歌翻译

打印到流

如何将字符串,整数,字符打印到标准输出流?

使用printf。第一个参数是格式字符串,其中包含要打印的数据的占位符。通用格式说明符是%s将参数视为 c 字符串指针,保持打印所有字符,直到达到 NULL 字符; %d将参数打印为整数; %p将参数打印为内存地址。

一个简单的例子如下所示:

char *name = ... ; int score = ...;
printf("Hello %s, your result is %d\n", name, score);
printf("Debug: The string and int are stored at: %p and %p\n", name, &score );
// name已经是一个字符串指针,指向字符串的开头第一个字节的地址。
// 我们需要用“&”操作符来获取整数变量score的地址。

默认情况下,为了提高性能,printf实际上不会写出任何内容(通过调用 write),直到其缓冲区已满或打印出换行符。

我怎么能打印字符串和单个字符?

使用puts( name );putchar( c ),其中 name 是指向 C 字符串的指针,c 只是char

如何打印到其他文件流?

使用fprintf( _file_ , "Hello %s, score: %d", name, score);其中 file 是预定义的'stdout''stderr'或fopenfdopen返回的 FILE 指针

我可以使用文件描述符吗?

是!只需使用dprintf(int fd, char* format_string, ...);只记得可以缓冲流,因此您需要确保将数据写入文件描述符。

如何将数据打印到 C 字符串中?

使用sprintf或更好snprintf

char result[200];
int len = snprintf(result, sizeof(result), "%s:%d", name, score);

~~snprintf 返回写入的字符数,不包括终止字节。在上面的例子中,这最多为 199。~~snprintf 返回有足够的空间写入字符串的长度,不包括末尾NULL字节。

char x[5];
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56"); // writes "1234" + null
printf("%d\n", size); // output 6

来源: this StackOverflow post 和 手册页。

如果我真的希望printf在没有换行符的情况下调用write怎么办?

使用fflush( FILE* inp )。将写入文件的内容。如果我想写没有换行的“Hello World”,我可以像这样写。

int main(){
    fprintf(stdout, "Hello World");
    fflush(stdout);
    return 0;
}

perror如何帮助?

假设您有一个失败的函数调用(因为您检查了手册页,它是一个失败的返回码)。 perror(const char* message)会将错误的英文版本打印到 stderr

int main(){
    int ret = open("IDoNotExist.txt", O_RDONLY);
    if(ret < 0){
        perror("Opening IDoNotExist:");
    }
    //...
    return 0;
}

解析输入

如何从字符串中解析数字?

使用long int strtol(const char *nptr, char **endptr, int base);long long int strtoll(const char *nptr, char **endptr, int base);

这些函数的作用是将指针指向您的字符串*nptrbase(即二进制,八进制,十进制,十六进制等)和可选指针endptr,并返回一个解析的 int。

int main(){
    const char *num = "1A2436";
    char* endptr;
    long int parsed = strtol(num, &endptr, 16);
    return 0;
}

但要小心!错误处理有点棘手,因为该函数不会返回错误代码。如果你传入一个会返回0的字符串而不是一个数字,这意味着你不能区别出一个合格的“0”和一个不合格的字符串。查看参考手册页去获得更多的关于strtol的不合法行为和超出边界的值。一个安全的替代是使用sscanf(并且检查返回值)。

int main(){
    const char *input = "0"; // or "!##@" or ""
    char* endptr;
    long int parsed = strtol(input, &endptr, 10);
    if(parsed == 0){
        // 不管输入的字符串是一个合格的10进制数还是真的是0

    }
    return 0;
}

如何使用scanf将输入解析为参数?

使用scanf(或fscanfsscanf)分别从默认输入流,任意文件流或 C 字符串获取输入。检查返回值以查看解析了多少项是个好主意。 scanf函数需要有效的指针。传递不正确的指针值是常见的错误来源。例如,

int *data = (int *) malloc(sizeof(int));
char *line = "v 10";
char type;
// 好习惯:确保scanf解析了line并读取了两个值
int ok = 2 == sscanf(line, "%c %d", &type, &data); // pointer error

我们想将字符值写入 c,将整数值写入 malloc 内存。但是我们传递了数据指针的地址,而不是指针指向的地址!所以sscanf会改变指针本身。即,指针现在将指向地址 10,因此该代码稍后将失败,例如当调用 free(数据)时。

如何阻止 scanf 导致缓冲区溢出?

以下代码假定 scanf 不会将超过 10 个字符(包括终止字节)读入缓冲区。

char buffer[10];
scanf("%s",buffer);

您可以包含一个可选的整数来指定排除终止字节的字符数:

char buffer[10];
scanf("%9s", buffer); // 读取至多9个字符从输入(第十个字节是留给终止字节的)

为什么gets很危险?我应该用什么呢?

以下代码容易受到缓冲区溢出的影响。它假定或信任输入行不超过 10 个字符,包括终止字节。

char buf[10];
gets(buf); // 请记住数组名代表着数组的第一个字节

gets在 C99 标准中已弃用,已从最新的 C 标准(C11)中删除。程序应使用fgetsgetline代替。

每个都分别具有以下结构:

char *fgets (char *str, int num, FILE *stream); 

ssize_t getline(char **lineptr, size_t *n, FILE *stream);

这是一种简单,安全的读取单行的方法。超过 9 个字符的行将被截断:

char buffer[10];
char *result = fgets(buffer, sizeof(buffer), stdin);

如果出现错误或文件结束,则结果为 NULL。注意,与gets不同,fgets将换行符复制到缓冲区中,您可能要将其丢弃 -

if (!result) { return; /* no data - don't read the buffer contents */}

int i = strlen(buffer) - 1;
if (buffer[i] == '\n') 
    buffer[i] = '\0';

我该如何使用getline

getline的一个优点是将在足够大小的堆上自动(重新)分配缓冲区。

// ssize_t getline(char **lineptr, size_t *n, FILE *stream);

 /* 初始化buffer和size的值,它们会被getline所改变 */
char *buffer = NULL;
size_t size = 0;

ssize_t chars = getline(&buffer, &size, stdin);

// 如果有换行符,就丢弃换行符
if (chars > 0 && buffer[chars-1] == '\n') 
    buffer[chars-1] = '\0';

// 读取另外一行
// 存在的缓冲区会被重复使用,如果有必要它会被释放并分配一个更大的缓冲区
chars = getline(&buffer, &size, stdin);

// 最后,不要忘了释放缓冲区内存
free(buffer);