在 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() 退格和多字节字符 的相关文章

  • 分层架构中的异常处理

    我们正在分层设计中重构 当然还有重新设计 我们的服务 我们有服务操作层 BLL 网络抽象层 gt 处理网络代理 数据抽象层 但我们对我们的异常处理策略有点困惑 我们不想向外界透露太多 BLL 的信息 从其他层到bll就可以了 我们不想让 t
  • C++ 返回值、引用、const 引用

    你能向我解释一下返回值 值引用和值常量引用之间的区别吗 Value Vector2D operator const Vector2D vector this gt x vector x this gt y vector y return t
  • 如何动态加载包含非托管代码的原始程序集?(绕过“无法验证的代码失败策略检查”异常)

    我将举一个使用的例子系统 Data SQLite DLL http sqlite phxsoftware com 这是一个包含非托管代码的混合程序集 如果我执行这个 var assembly Assembly LoadFrom System
  • 如何在线程创建和退出时调用函数?

    include
  • 在调用堆栈中看到大量 clr!CLR Semaphore::Wait

    我们看到很多像下面这样的调用堆栈 我可以知道什么条件 情况会发生这种情况吗 OS Thread Id 0x48654 559 Current frame ntdll NtWaitForSingleObject 0xa Child SP Re
  • 代码块 power 函数在 c 中不起作用

    我正在使用代码块来学习c 我的代码是 include
  • 宏可以按参数数量重载吗?

    如何this https stackoverflow com q 9183993 153285工作 如何实现 C99 C 11 可变参数宏以仅根据为其提供多少个参数来扩展到不同的事物 编辑 请参阅末尾以获得现成的解决方案 要获得重载的宏 首
  • 有没有办法将 boost::json::serializer 切换为美化输出?

    Using boost json serializer如中的示例所示文档 快速查看 http vinniefalco github io doc json json usage quick look html以紧凑格式保存 json tre
  • 使用 size_t 值反向遍历向量

    我想以相反的方向遍历向量的值 如您所知 向量的大小为 size t 当我使用以下代码时 for size t r m size 1 r gt 0 r x r f r for size t c r 1 c lt m size c x r m
  • C++:初始化静态字符串成员

    我在 C 中初始化静态字符串成员时遇到一些问题 我有几个类 每个类都包含几个表示 id 的静态字符串成员 当我通过调用静态函数初始化变量时 一切都很好 但是 当我想为一个变量分配另一个变量的值时 它仍然保留空字符串 这段代码有什么问题 st
  • 如何填充两个样条线或直线系列之间的区域

    我有这个Chart 如何填充两个之间的区域Series S0 and S1 说蓝色和黄色Series 为此 我们编写了其中之一Paint事件 这里的ValueToPixelPosition https msdn microsoft com
  • 如何解决内存碎片

    我们偶尔会遇到这样的问题 长时间运行的服务器进程 在 Windows Server 2003 上运行 由于内存分配失败而引发异常 我们怀疑这些分配由于内存碎片而失败 因此 我们一直在寻找一些可能对我们有帮助的替代内存分配机制 我希望有人能告
  • char* argv[] 在 c/c++ 中如何工作? [复制]

    这个问题在这里已经有答案了 我知道它用于使用命令行中的参数 但我没有得到声明 字符 argv 它是否意味着指向 char 数组的指针 如果是的话为什么没有大小 如果不是动态数组 就不需要有大小吗 我做了一些研究 发现有人说它会衰减为 cha
  • 参数数量在编译时确定的 Lambda 函数

    我想声明一个带有 N 个参数的 lambda 函数 其中 N 是模板参数 就像是 template
  • 为什么我不能对普通变量进行多态?

    我是一名Java程序员 最近开始学习C 我对某事感到困惑 据我了解 在 C 中 要实现多态行为 您必须使用指针或引用 例如 考虑一个类Shape与实施的方法getArea 它有几个子类 每个子类都以不同的方式重写 getArea 然后考虑以
  • 字符串 c 的二叉树

    我正在尝试实现一个能够在 c 中保存字符串的二叉树 在让代码适用于整数之后 我尝试稍微修改它以处理字符数组 现在我似乎完全破解了代码 但不知道如何破解 任何帮助表示赞赏 include
  • OpenGL 计算着色器调用

    我有一个与新计算着色器相关的问题 我目前正在研究粒子系统 我将所有粒子存储在着色器存储缓冲区中 以便在计算着色器中访问它们 然后我派遣一个一维工作组 define WORK GROUP SIZE 128 shaderManager gt u
  • 如何在 Winform DataGridView 中创建不同的单元格格式

    我有一个 DataGridView 我将其绑定到 DataTable DataTable 是一个全数字值 要求 DataGridView 中的每 n 行都包含文本 而不是数值 以便在视觉上为用户分隔部分 我很高兴在绑定后将此文本数据放入 D
  • 清理堆分配对象的良好实践或约定?

    我正在学习C 我有 C C ObjC 背景 相当高级的语言 在 C 或 ObjC 上 作为函数或方法的结果返回堆分配的对象是很简单的 因为对象的清理是受管理的 按照惯例 会在适当的时候销毁 但我不知道在 C 中应该如何处理这个问题 例如 s
  • C# amo 获取角色完整

    我正在开发一个 SSAS 项目 其中除其他事项外 我需要获取 C 中表格多维数据集的完整用户列表 目前我让它以这样的方式工作 我可以获得角色 但数据不完整 当我调用 Server Database Roles 为了便于阅读而简化 属性并枚举

随机推荐