gpt4 book ai didi

c - 使 stdin 以安全且可移植的方式可写

转载 作者:行者123 更新时间:2023-11-30 18:55:44 25 4
gpt4 key购买 nike

我试图运行两个程序。

<强> Case 1

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
int n;
int k = 10;
int ret_val = 0;
ret_val = write (0, &k, sizeof(int));
if (-1 == ret_val)
{
printf ("Failed to write");
exit (EXIT_FAILURE);
}
scanf ("%d", &n);
printf ("Integer read is %d \n", n);
return 0;
}

然后我尝试了下一个。

<强> Case 2

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
int n;
int k = 10;
int ret_val = 0;

/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == source_fd)
{
printf ("Failed to open file for reading");
exit (EXIT_FAILURE);
}
int stdin_fd;

/* Close STDIN_FILENO */
close(0);
/* dup the source */
stdin_fd = dup (source_fd);
if (-1 == stdin_fd)
{
printf ("Failed to dup");
exit (EXIT_FAILURE);
}

/* write to stdin_buffer (content will be taken from file_in.txt) */
ret_val = write (stdin_fd, &k, sizeof(int));
if (-1 == ret_val)
{
printf ("Failed to write to stdin_buffer");
exit (EXIT_FAILURE);
}

scanf ("%d", &n);
printf ("Integer read is %d \n", n);

close(source_fd);
return 0;
}

现在,在第一种情况下,我无法写入标准输入。在第二种情况下,我能够从文件“file_in.txt”获取输入,并将内容发送到标准输入缓冲区。

对于为什么我的第一个案例没有成功,我无法得到很好的解释。有人可以解释一下吗?

stdin 应该像任何其他文件一样,对吗?如果是写保护就好了。但是当我重定向输入时(在第二种情况下),没有“权限被拒绝”问题。该代码似乎不可移植。是否有一种可移植且安全的方法来从文件重定向标准输入?

<小时/>

在查看了评论后,我想出了一个更好的工作代码。我想要一些关于此代码的反馈

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define LEN 100

int main()
{
int n;
char buffer[LEN];
memset (buffer, '\0', LEN);
int ret_val = 0;

/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
{
perror ("Failed to open file for reading");
exit (EXIT_FAILURE);
}
/* Temp stdin_buffer */
int temp_fd = open ("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
{
perror ("Failed to open temp stdin");
exit (EXIT_FAILURE);
}

int stdin_fd;
/* Close STDIN_FILENO */
close(0);
/* dup the source */
stdin_fd = dup (temp_fd);
if (-1 == stdin_fd)
{
perror ("Failed to dup");
exit (EXIT_FAILURE);
}


ret_val = read (source_fd, buffer, LEN);
if (-1 == ret_val)
{
perror ("Failed to read from source");
exit (EXIT_FAILURE);
}
else
{
printf ("%s read from Source file\n", buffer);
}

/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write (stdin_fd, buffer, LEN);
if (-1 == ret_val)
{
perror ("Failed to write to stdin_buffer");
exit (EXIT_FAILURE);
}


ret_val = lseek (stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
{
perror ("Failed lseek");
exit (EXIT_FAILURE);
}

ret_val = scanf ("%d", &n);
if (-1 == ret_val)
{
perror ("Failed to read stdin_buffer");
exit (EXIT_FAILURE);
}
printf ("Integer read is %d \n", n);

close(source_fd);
return 0;
}

最佳答案

更新之前

在第一个程序中,3 个空字节和一个换行符(可能)被写入屏幕(不一定按照这个顺序);然后程序尝试从键盘读取(假设命令行上没有 I/O 重定向)。写入标准输入不会加载输入缓冲区。您经常可以写入标准输入(并从标准输出和标准错误读取),因为经典技术使用 O_RDWR 打开文件描述符。然后将其连接到标准 I/O channel 。但是,不能保证您可以这样做。 (顺便说一句,第一个程序需要 <unistd.h> 。)

第二个程序有太多未定义的行为,很难分析。 open() call 需要三个参数,因为它包含 O_CREAT ;第三个参数是文件的模式(例如 0644 )。您没有检查open()成功了。您不检查写入是否成功;它不会,因为文件描述符已打开 O_RDONLY (或者更确切地说, source_fd 打开 O_RDONLY ,并且 dup() 将该模式复制到文件描述符 0),这意味着 write()将失败。不检查输入操作(您不确保 scanf() 成功)。 (第二个程序实际上并不需要 <sys/types.h><sys/stat.h> 。)

基本上,您对正在发生的事情一无所知,因为您没有检查任何关键函数调用。

更新 1 后

请注意,错误消息应写入标准错误,并应以换行符终止。

我让第一个程序按所述工作(Mac OS X 10.10 Yosemite,GCC 4.8.1),尽管很难证明空字节已写入标准输入(但在那里写入了换行符)。然后我可以输入 10(或 20、或 100、或……)加上 Return,然后就会打印该整数。

第二个程序在 scanf() 上失败因为当您尝试读取时,文件指针位于文件末尾。您可以通过程序的这个变体看到:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(void)
{
/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open ("file_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == source_fd)
{
printf ("Failed to open file for reading\n");
exit (EXIT_FAILURE);
}

close(0);
int stdin_fd = dup (source_fd);
if (-1 == stdin_fd)
{
printf("Failed to dup\n");
exit(EXIT_FAILURE);
}

int k = 10;
int ret_val = write(stdin_fd, &k, sizeof(int));
if (-1 == ret_val)
{
printf("Failed to write to stdin_buffer\n");
exit(EXIT_FAILURE);
}

int rc;
int n;
if ((rc = scanf("%d", &n)) != 1)
printf("Failed to read from standard input: rc = %d\n", rc);
else
printf("Integer read is %d (0x%08x)\n", n, n);

close(source_fd);
return 0;
}

它产生:

Failed to read from standard input: rc = -1

如果在读取之前倒回文件,则会返回 0;写入文件的二进制数据不是整数的有效字符串表示形式。

更新 2 后

我写了一个小函数err_exit()因为它允许页面上的代码更小。我在几个地方修改了您的代码以报告先前函数的返回值。缺少输入并不是错误。当您读取 0 字节时,这不是错误;而是错误。是EOF。当有数据要读取但不是整数值的文本格式时,不会发生任何转换,但这不是错误。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define LEN 100

static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}

int main(void)
{
int n = 99;
char buffer[LEN];
memset(buffer, '\0', LEN);
int ret_val = 0;

/* Open file from which content shall be inserted to stdin_buffer */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin_buffer */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");

/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");

ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);

/* write to stdin_buffer (content taken from file_in.txt) */
ret_val = write(stdin_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to write to stdin_buffer");

ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");

ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin_buffer");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);

close(source_fd);
return 0;
}

输出:

(0 bytes) <<>> read from Source file
Integer read is 99 (ret_val = 0)

何时 scanf()无法读取值,它(通常)不会将任何内容写入相应的变量。这就是99幸存下来的原因。如果你想要scanf()可以读取的数据作为整数,您需要:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define LEN 100

static void err_exit(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}

int main(void)
{
int n = 99;
char buffer[LEN] = "";
int ret_val = 0;

/* Open file from which content shall be inserted to stdin */
int source_fd = open("file_in.txt", O_CREAT | O_RDONLY, S_IRWXU);
if (-1 == source_fd)
err_exit("Failed to open file for reading");
/* Temp stdin */
int temp_fd = open("temp_in.txt", O_CREAT | O_RDWR, S_IRWXU);
if (-1 == temp_fd)
err_exit("Failed to open temp stdin");

/* Close STDIN_FILENO */
close(0);
/* dup the source */
int stdin_fd = dup(temp_fd);
if (-1 == stdin_fd)
err_exit("Failed to dup");

ret_val = read(source_fd, buffer, LEN);
if (-1 == ret_val)
err_exit("Failed to read from source");
else
printf("(%d bytes) <<%s>> read from Source file\n", ret_val, buffer);

/* write to stdin (content taken from file_in.txt) */
ret_val = write(stdin_fd, "10\n", sizeof("10\n")-1);
if (-1 == ret_val)
err_exit("Failed to write to stdin");

ret_val = lseek(stdin_fd, 0, SEEK_SET);
if (-1 == ret_val)
err_exit("Failed lseek");

ret_val = scanf("%d", &n);
if (-1 == ret_val)
err_exit("Failed to read stdin");
printf("Integer read is %d (ret_val = %d)\n", n, ret_val);

close(source_fd);
return 0;
}

输出:

(0 bytes) <<>> read from Source file
Integer read is 10 (ret_val = 1)

关于c - 使 stdin 以安全且可移植的方式可写,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26696811/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com