使用 write() 将两个字符串连接到 C 中的标准输出

Posted

技术标签:

【中文标题】使用 write() 将两个字符串连接到 C 中的标准输出【英文标题】:Concatenate two strings using write() to stdout in C 【发布时间】:2022-01-10 19:35:14 【问题描述】:

我正在尝试通过两个不同的进程(父亲和孩子)将两个字符串逐个字符地写入标准输出,强制使用 read() 和 write(),代码如下:

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

char buf1[100];
char buf2[100];
int i;

pid_t t;

int main()

    printf("String1:\n");
    read(0, buf1, 100);
    printf("String2:\n");
    read(0, buf2, 100);
    
    t = fork();
    
    if (t > 0) 
        wait(NULL);
        for (i = 0; i < strlen(buf2); i++) 
            write(1, &buf2[i], 1);
            sleep(1);
            
        
    else 
        for (i = 0; i < strlen(buf1); i++) 
            write(1, &buf1[i], 1);
            sleep(1);
            
        exit(0);
        
    return(0);

我设法让两个进程分别将一个字符串写入标准输出,但我未能将这两个字符串合并为一个。我尝试使用 ANSI 转义码让光标为第一个字符串结束的第二个字符串做好准备,但是如何将它们集成到 write() 内的缓冲区中?

现在,通过上面提到的代码,我可以得到这个输出:

String1:
Hyper
String2:
loop
Hyper
loop

我想获得:

String1:
Hyper
String2:
loop
Hyperloop

提前谢谢你

【问题讨论】:

从您阅读的字符串中删除换行符,然后在完成时添加一个。此外,您应该使用来自read()write() 的返回值来确定输入和输出的长度。你对短字符串没问题——但如果有人输入了超过 100 个字符的输入,你可能会得到一些奇怪的输出。此外,一次写一个字符是愚蠢的。 你为什么fork()ing?你为什么sleep()ing? read(0, buf1, 100); 读取 100 个输入字符 - 它不会在 '\n' 处停止。它不形成字符串。研究fgets(). @chux-ReinstateMonica — 如果标准输入来自终端,read() 调用通常只会读取到第一个换行符(假设终端处于规范模式并且没有数据排队等待阅读;如果输入行长于 100 个字符,您当然只能得到 100 个字符(没有空字节)。如果输入不是来自终端,则您的观察是正确的。这取决于终端驱动程序。 是的@JonathanLeffler,从第一个字符串中删除换行符对于找到解决方案至关重要。我通过写除最后一个缓冲区之外的第一个缓冲区的所有字节来完成它。非常感谢您的帮助。 【参考方案1】:

@chux 已经解决了主要问题。以下是相应的工作代码,并进行了以下其他更改:

    本地化或消除变量 添加了缺失的包含以使其在 Linux 上编译 将每个字符的 write 替换为 sleep ,整个字符串只有一个 write 防止缓冲区溢出 定义 LEN 而不是内联魔法值,也使用 STDOUT_FILENO
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>

#define LEN 99
#define str(s) xstr(s)
#define xstr(s) #s

int main() 
    char buf1[LEN+1];
    char buf2[LEN+1];

    printf("String1:\n");
    scanf("%" str(LEN) "s", buf1);
    printf("String2:\n");
    scanf("%" str(LEN) "s", buf2);

    if (fork() > 0) 
        wait(NULL);
        write(STDOUT_FILENO, buf2, strlen(buf2));
     else 
        write(STDOUT_FILENO, buf1, strlen(buf1));
        exit(0);
    
    return(0);

这是输出(注意最后一行后没有换行符):

String1:
Hyper
String2:
loop
Hyperloop

不知道为什么要一次打印一封信,但更好的选择是 printf("%s", buf)(但是 buf1buf2)。

【讨论】:

或者,由于 write() 似乎是输出的强制要求,write(1, buf1, strlen(buf1))write(STDOUT_FILENO, buf1, strlen(buf1))(同样适用于 buf2)。 还要注意我的comment 到主要问题。 我建议使用 printf() 而不是 write(),因此很容易在 buf2 之后的格式字符串中标记 \nputchar() 或另一个 write() 也很容易,所以不确定这个论点有多好。我最近玩过无缓冲 (write()) 与缓冲写入 (fwrite()) 的对比,我很惊讶在性能上有如此大的差异。显然,这不是性能敏感代码。 一次一个字符 I/O 是可行的,但速度较慢。即使是 16 字节的缓冲区也能显着提高性能。通常,在 1-4 KiB 范围内的某个地方可以为您提供良好的性能 - 更大的缓冲区大小会购买有限的额外性能,但会使用更多空间。有趣的是,这往往是标准 I/O 库 (&lt;stdio.h&gt;) 倾向于放置其缓冲区大小的地方。 @JonathanLeffler 您显然是对的,重新读取以换行符终止,但这意味着我必须修复 STDIN_FILENO 而不是 0,检查读取的返回码(对于 -1 和 >1),并且覆盖 '\n' (或保持缓冲区长度无论如何都是合理的),确保尾随 '\0' 可能是一个很好的衡量标准。 scanf 似乎是一条更直接的路线。【参考方案2】:

我想我找到了解决办法。 read() 读取或 write() 写入的最后一个字节对应于换行符。因此,写入第一个缓冲区的所有字节,但其中一个对于获得我想要的结果至关重要。这样,第一个缓冲区的 for 循环如下:

for (i = 0; i < strlen(buf1)-1; i++) 
            write(1, &buf1[i], 1);
            sleep(1);
            

完整的代码如下:

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

char buf1[100];
char buf2[100];
int i;

pid_t t;

int main()

    printf("String1:\n");
    read(0, buf1, 100);
    printf("String2:\n");
    read(0, buf2, 100);
    
    t = fork();
    
    if (t > 0) 
        wait(NULL);
        for (i = 0; i < strlen(buf2); i++) 
            write(1, &buf2[i], 1);
            sleep(1);
            
        
    else 
        for (i = 0; i < strlen(buf1)-1; i++) 
            write(1, &buf1[i], 1);
            sleep(1);
            
        exit(0);
        
    return(0);

非常感谢帮助者!

【讨论】:

1) 我是 certianl read(0, buf1, 100); 肯定不会形成 string 因为它可能缺少 null 字符,所以稍后 strlen(buf1) 是半身像。 2)修复后,考虑i &lt; strlen(buf1)-1和令人惊讶的buf[0] == 0的影响。然后代码就像i &lt; SIZE_MAX。最好是i + 1 &lt; strlen(buf1)。像len = read(....) ... i &lt; len 这样的东西会更好。 非常感谢@Reinstate Monica。问候 默认情况下,您的终端是行缓冲的,这意味着如果您输入超过 200 个字节,您最终会将剩余的输入发送到您的 shell,它会尝试执行该操作。跨度>

以上是关于使用 write() 将两个字符串连接到 C 中的标准输出的主要内容,如果未能解决你的问题,请参考以下文章

如何将字符串连接到刀片中的 $ 变量?

将字符串连接到 pig 中的字段

将字符串连接到列表[重复]

如何将子查询中的字符串连接到mysql中的一行?

如何将javascript函数中的参数字符串连接到附加html中的字符串上?

Spark - 将每个分区的字符串连接到单个字符串