在 C 中使用 getchar() 退格和多字节字符

2024-02-02

我正在阅读 BeeJ 的 C 编程指南并复制了他的 readline() 函数,该函数从 stdin 读取一行。由于它的实现方式,它在读取多字节字符时没有问题,因为它根据接收到的字节总数重新分配空间,因此,它在处理 unicode 输入时没有问题。这是一个包含以下功能的程序:

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

#define printPrompt printf("db > ")

/* The readLine function, allocates memory for a short string and
** reads characters into it. When the string's size limit is met,
** the same memory block is reallocated, but twice the size.
** Shamelessly stolen from BeeJ's guide to C programming |=
*/ 

char* read_line(void) {
    int i = 0; /* Position of the current character */
    int linbuf = 4; /* Size of our line in memory, will be
    duplicated once the line length surpasses it */
    char* lin; /* The pointer value to our line */
    int c; /* The value we'll use to accept characters */

    if(  !(lin = malloc( linbuf*sizeof(char)))  )
        return NULL;

    while( c = getchar(), c != '\n' && c != EOF ) {
        /* Check if the amount of bytes accepted has surpassed the
         * amount of memory we've allocated so far */
        if(i == linbuf - 1) {
            /* If it did, reallocate double the space */
            linbuf *= 2;
            char* tmpbuf = realloc(lin, linbuf);
            /* If the space couldn't have been allocated then we'd
             * run out of memory. Delete everything and abort. */
            if(tmpbuf == NULL) {
                free(tmpbuf);
                return NULL;
            }
            /* If we've arrived here that means there were no
             * problems, so we'll assign the newly reallocated
             * memory to "lin" */
            lin = tmpbuf;
        }
        /* Add the new character to our allocated space */
        lin[i++] = c;
    }
    /* If we've received an EOF signal after having read 0
     * characters, we'd like to delete our allocated memory and
     * return a NULL */
    if(c == EOF && i == 0) {
        free(lin);
        return NULL;
    }
    /* Here we'll shrink the allocated memory to perfectly fit our
     * string */
    if(i < linbuf - 1) {
        char* tmpbuf = realloc(lin, i + 1);
        if(tmpbuf != NULL)
            lin = tmpbuf;
    }
    /* Here we'll terminate the string */
    lin[i] = '\0';

    /* Finally, we'll return it */
    return lin;
}

int main(int argc, char* argv[]) {
    char* hey = read_line();
    printf("%s\n", hey);
    return 0;
}

输入为
Hello, World! (:
会导致输出
Hello, World! (:

多字节字符的输入,例如
שלום, עולם! (:
将导致正确的输出
שלום, עולם! (:

但是,如果我按退格键,只会删除一个字节字符,导致输出乱码;输入(退格标记为 \b):
שיהיה לכם בוקר טוב\b\b\b\b\b\b\b\bערב טוב
这应该最终是:
שיהיה לכם ערב טוב
实际上最终是:
�שיהיה לכם בוק�ערב טוב

我的计算机运行 Void Linux 的 Musl-libc 版本,我使用 tcc 和 gcc 编译该程序,两者都产生相同的结果。

这个问题是否与我的 libc、我的终端(suckless st)、我的内核有关,或者是我在代码中缺少的东西?无论发生什么情况,我有什么办法可以处理它,最好不使用任何外部库,例如 ICU 或你有什么?


“有什么方法可以在不使用任何外部库的情况下处理它[...]”答案是否定的。除非您准备自己编写一个大型且复杂的库,否则就是这样。

对于外部库,这很简单:

sudo apt install libreadline-dev # no idea how to say that in Void

    #include <stdio.h>
    #include <readline/readline.h>
    #include <readline/history.h> // optional, to enable line history

    int main()
    {
        using_history(); // optional
        char* s;
        while ((s = readline("Type something > ")))
        {
            printf("You have typed: %s\n", s);
            add_history(s);
        }
        printf ("Bye!\n");
    }

有了这个,您可以免费获得很多好东西,包括完整的 Unicode 感知行编辑、可编程键绑定和输入历史记录。

Edit在我第一次检查的机器上,您的程序的行为就像您所描述的那样。但在另一台机器上,也就是我的家庭桌面上,它按预期工作,根本没有奇怪的退格行为。我检查了 X11 终端和文本 linux tty。所以我想毕竟有一些内核和/或终端。

稍后编辑有一个stty控制此行为的设置,至少对于 UTF-8 而言。

stty iutf8

并且您的程序应该按预期运行,不需要大型的胖库。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 C 中使用 getchar() 退格和多字节字符 的相关文章

  • 模板类包装任意类型/非类型模板类

    假设我有一个模板类base和一个班级wrapper其中包含一个实例化成员base 我想定义班级wrapper这样它依赖于模板参数包 该参数包只是 传递 给实例化成员base 例如 考虑下面的代码 它工作得很好 include
  • C#中如何检测字符串是否为货币

    通常当我需要转换时currency string 如 1200 55 z 或 1 249 到十进制值我这样做 if currencyString Contains z decimal value Decimal Parse dataToCh
  • 如何向 UWP 项目添加 .NET dll 引用?

    我有几个适用于 NETv4 x 的 NET dll 项目 我将版本更改为 4 6 1 并重新构建 没有出现问题 当我尝试从 UWP 项目向它们添加引用时 出现错误 项目的目标是 NETCore 而文件引用的目标是 NET框架 这不是受支持的
  • 如何从当前 .NET 表单/应用程序发送密钥 F12

    我非常确定以下按钮激活的表单代码应该在我的 C 应用程序中引发 Control F12 SendKeys F12 但它似乎并没有继续进入 Windows shell 并激活另一个正在侦听它的程序 我的键盘可以用 看起来发送键在某处被拦截 并
  • 测试 hdf5/c++ 中的组是否存在

    我正在打开一个现有的 HDF5 文件来附加数据 我想向那个叫做的小组保证 A存在以供后续访问 我正在寻找一种简单的方法来创建 A有条件地 如果不存在则创建并返回新组 或者返回现有组 一种方法是测试 A存在 我怎样才能高效地做到这一点 根据
  • 我应该在单元测试中使用 AutoMapper 吗?

    我正在为 ASP NET MVC 控制器方法编写单元测试 这些控制器依赖于IMapper 我创建的用于抽象 AutoMapper 的接口 使用 Castle Windsor 通过构造函数注入传入 动作方法使用IMapper从领域对象映射到
  • 阅读 Stack Overflow RSS 源

    我正在尝试获取未回答问题的列表the feed https stackoverflow com feeds 但我在阅读时遇到困难 const string RECENT QUESTIONS https stackoverflow com f
  • 使用 VSTO 更改 Outlook 设置

    我刚刚花了大约 4 个小时试图弄清楚如何以编程方式检索 设置 Microsoft Outlook 2010 的 Outlook 设置 我所说的 设置 是指文件 选项 邮件下的设置 我想做的是检索用户设置的设置列表 自动化我们每天需要在某些消
  • Qt中正确的线程方式

    我的图像加载非常耗时 图像很大 并且在加载时也完成了一些操作 我不想阻止应用程序 GUI 我的想法是在另一个线程中加载图像 发出图像已加载的信号 然后用该图像重绘视图 我的做法 void Window loadImage ImageLoad
  • 根据拦截和返回值自动重试客户端WCF调用

    是否可以拦截 WCF 调用的结果并重试该操作 例如 操作的返回值可能包含状态代码 指示我传递到原始调用的会话令牌已过期 在这种情况下 我可以检索新的会话令牌并使用新的会话令牌重试调用 是否可以通过使用 WCF 拦截返回值 检查它 然后以对操
  • 防止复制构造和返回值引用的分配

    如果我有一个函数返回对类实例的引用 但我无法控制其源 比如说list
  • CMake - 将预构建库链接到 C# 项目

    我正在使用 CMake 构建 C 库 该库依赖于已构建的库 dll 我似乎无法让图书馆链接到我的图书馆 我尝试过使用target link libraries mylib external lib 我也尝试过暴力破解 reference e
  • 如何在不使用reinterpret_cast的情况下使用dlsym()加载函数?

    我正在尝试使用 clang tidy 来强制执行 C 核心指南 虽然它确实有很多有效点 但有一件事我无法真正解决 dlsym 返回一个void 我需要以某种方式将其转换为正确的函数指针 为此 我使用reinterpret cast 由于指南
  • for 循环 - 没有效果的语句

    由于某种原因 我收到错误 statement with no effect关于这个声明 for j idx j lt iter j increment printf from loop idx i int idx punc ctxt j 你
  • 在生产者-消费者情况下使用条件变量

    我正在尝试了解条件变量以及如何在生产者 消费者情况下使用它 我有一个队列 其中一个线程将数字推入队列 而另一个线程从队列中弹出数字 当生产线程放置一些数据时 我想使用条件变量向消费线程发出信号 问题是有时 或大多数时候 它只将最多两个项目推
  • C#:如何使用 SHOpenFolderAndSelectItems [重复]

    这个问题在这里已经有答案了 有人可以举例说明如何使用 shell 函数吗SH打开文件夹并选择项目 http msdn microsoft com en us library bb762232 VS 85 aspx来自 C 我不太明白如何使用
  • 在 MVVM 中,可以在视图后面的代码中访问 ViewModel 吗?

    在 MVVM 模式中 是否可以接受甚至可以访问视图代码后面的 ViewModel 属性 我有一个可观察的集合 它填充在 ViewModel 中 我需要在视图中使用它来绑定到带有链接列表的无限滚动条 IE private LinkedList
  • 从 C# 中的 .NET SecureString 读取单个字符?

    WPF 的PasswordBox 返回一个SecureString 它对窥探者隐藏密码 问题是你最终必须获得密码的值 而我在网上找到的建议都涉及将值复制到字符串中 这会让你回到窥探者的问题 IntPtr bstr Marshal Secur
  • 如何使复选框不可选择?

    我想知道你是怎么做的CheckBox在c 中无法选择 我认为这会是类似 SetSelectable false 之类的东西 但我似乎看不到该方法 I found CanSelect但这似乎是只读属性 您可以设置自动检查 http msdn
  • ASP.NET Core:会话 ID 始终变化

    今天启动了一个全新的 ASP NET Core 网站 按照说明添加会话 我们在索引页上打印出会话 ID 它始终是唯一的 我认为这可能是 cookie 合规性 所以我在 Chrome 的高级设置和调试器中删除了所有 cookie 但横幅不会再

随机推荐