实现一个简单的行编辑器(优化)

时间:2024-02-29 17:22:16

0.目录

1.前言

2.使用方向键来实现光标左右移动

3.按两下ESC键退出程序

4.移动光标到行首

5.移动光标到行尾

6.总代码

1.前言

之前已经写过一篇文章了:实现一个简单的行编辑器
实现的功能有:
1.按下大小写字母或者数字的时候,显示在屏幕上
2.可以使用退格键来删除前一个字符
3.可以使用Del键来删除一行
4.使用\'[\'键来实现光标左移
5.使用\']\'键来实现光标右移

但是还是有很多不完善的地方。本篇文章修改以及添加了以下功能:
1.使用方向键\'←\'键来实现光标左移
2.使用方向键\'→\'键来实现光标右移
3.按两下ESC键退出程序
4.使用\'[\'键来实现移动光标到行首
5.使用\']\'键来实现移动光标到行尾

并且在之前的文章中,采用的是putchar(\'\b\');实现光标左移,使用输出当前字符实现光标右移。
在这篇文章中,将更改这种方法。

2.使用方向键来实现光标左右移动

在之前的文章中提到过。方向键其实是一次性输入了三个字符才组成的。
以下是对应关系:
上:0x1b 0x5b 0x41
下:0x1b 0x5b 0x42
左:0x1b 0x5b 0x44
右:0x1b 0x5b 0x43
Del键:0x1b 0x5b 0x33 0x7e
其中,0x41、0x42、0x43、0x44分别就是大写字母A、B、C、D。
在本篇文章中不实现上和下的功能。

//连续输出三个字符实现左移光标
putchar(0x1b);
putchar(0x5b);
putchar(0x44);
//连续输出三个字符实现右移光标
putchar(0x1b);
putchar(0x5b);
putchar(0x43);

在while循环中实现:

else if ( c == 0x1b )
{
    c=getchar();
    if ( c == 0x5b )
    {
        c=getchar();
        if ( c == 0x44 && p )
        {
            //连续输出三个字符实现左移光标
            putchar(0x1b);
            putchar(0x5b);
            putchar(0x44);
            p--;
        }
        else if ( c == 0x43 && p < len )
        {
            //连续输出三个字符实现右移光标
            putchar(0x1b);
            putchar(0x5b);
            putchar(0x43);
            p++;
        }
        else continue;
    }
    else continue;
}

这里使用了一个叫做状态机的概念。
有兴趣的可以去找找相关的资料了解一下。

3.按两下ESC键退出程序

只需要在上面的程序中加入一段代码就行了。
顺便将Del键也改一下:

else if ( c == 0x1b )
{
    c=getchar();
    if ( c == 0x5b )
    {
        c=getchar();
        if ( c == 0x44 && p )
        {
            //连续输出三个字符实现左移光标
            putchar(0x1b);
            putchar(0x5b);
            putchar(0x44);
            p--;
        }
        else if ( c == 0x43 && p < len )
        {
            //连续输出三个字符实现右移光标
            putchar(0x1b);
            putchar(0x5b);
            putchar(0x43);
            p++;
        }
        else if ( c == 0x33 )
        {
            c=getchar();
            if ( c == 0x7e )
            {
                //删除键(Del):删除整行(user input delete)
                //1.从光标处移动到结尾
                while( ++p <= len )
                    putchar(\' \');
                //2.从结尾往前依次退格
                while( --p )
                    fun_backspace();
                //3.len置0
                len = 0;
            }
            else continue;
        }
        else continue;
    }
    else if ( c == 0x1b )
    {
        //按了两次ESC键,退出程序
        break;
    }
    else continue;
}

4.移动光标到行首

使用\'[\'键来实现移动光标到行首

else if ( c == \'[\' && p )
{
    //使用\'[\'移动光标到行首
    while( p-- )
        fun_left();
    p = 0;
}

5.移动光标到行尾

使用\']\'键来实现移动光标到行尾

else if ( c == \']\' && p < len )
{
    //使用\']\'移动光标到行尾
    while( p++ < len )
        fun_right();
    p = len;
}

6.总代码

/* 设计完成一个行编辑器
 * 能够接受用户输入,能倒退删除,插入,移动光标等
 */
#include   <stdio.h>
#include   <termios.h>

#define oops(s, x) { perror(s); exit(x); }

void fun_set(struct termios *info, char set);//设置回显位,设置缓冲
void fun_backspace();//实现退格功能
void fun_left();//实现光标左移
void fun_right();//实现光标右移

int main()
{
    int c;
    int i, j;

    struct termios info;
    fun_set( &info, 0 );//关掉回显位,关掉缓冲
    
    char str[30];//保存输出的字符
    int p = 0;//当前位置
    int len = 0;//总长度

    while( ( c=getchar() ) != EOF )
    {
        if( isalnum(c) )
        {
            //isalnum()函数:如果c是一个数字或字母返回非0值,否则为0
            //user input a letter or a number
            //1.将当前位置之后的值依次后移
            j = ++len;
            while( j-- > p )
                str[j] = str[j-1];
            str[p] = c;
            j = len - p - 1;//光标要移动的距离
            //2.从当前位置开始重新输出数组
            while( p < len )
                putchar(str[p++]);
            //3.将光标移动到之前的位置
            while( j-- > 0 && p-- )
                fun_left();
        }
        else if( c == 0x7f && p )
        {
            //退格键(user input a backspace)
            j = len - p;//光标要移动的距离
            //1.将当前位置之后的值依次前移
            putchar(\'\b\');
            while( p < len )
            {
                str[p-1] = str[p];
                putchar(str[p]);
                p++;
            }
            //2.将最后一个元素删除
            putchar(\' \');
            putchar(\'\b\');
            len--;
            p--;
            //3.将光标移动到之前的位置
            while( j-- > 0 && p-- )
                fun_left();
        }
        else if ( c == 0x1b )
        {
            c=getchar();
            if ( c == 0x5b )
            {
                c=getchar();
                if ( c == 0x44 && p )
                {
                    //连续输出三个字符实现左移光标
                    fun_left();
                    p--;
                }
                else if ( c == 0x43 && p < len )
                {
                    //连续输出三个字符实现右移光标
                    fun_right();
                    p++;
                }
                else if ( c == 0x33 )
                {
                    c=getchar();
                    if ( c == 0x7e )
                    {
                        //删除键(Del):删除整行(user input delete)
                        //1.从光标处移动到结尾
                        while( ++p <= len )
                            putchar(\' \');
                        //2.从结尾往前依次退格
                        while( --p )
                            fun_backspace();
                        //3.len置0
                        len = 0;
                    }
                    else continue;
                }
                else continue;
            }
            else if ( c == 0x1b )
            {
                //按了两次ESC键,退出程序
                break;
            }
            else continue;
        }
        else if ( c == \'[\' && p )
        {
            //使用\'[\'移动光标到行首
            while( p-- )
                fun_left();
            p = 0;
        }
        else if ( c == \']\' && p < len )
        {
            //使用\']\'移动光标到行尾
            while( p++ < len )
                fun_right();
            p = len;
        }
    }

    fun_set( &info, 1 );//打开回显位,打开缓冲
}

void fun_set(struct termios *info, char set)
{
    if ( tcgetattr(0, info) == -1 )          /* get attribs   */
        oops("tcgettattr", 1);
    /*set为1,打开回显位,打开缓冲;set为0,关掉回显位,关掉缓冲*/
    if( set )
    {
        (*info).c_lflag |= ECHO;    /* turn on bit   */
        (*info).c_lflag &= ICANON;  /* turn on bit   */
    }
    else
    {
        (*info).c_lflag &= ~ECHO;   /* turn off bit   */
        (*info).c_lflag &= ~ICANON; /* turn off bit   */
    }
    if ( tcsetattr(0, TCSANOW, info) == -1 ) /* set attribs    */
        oops("tcsetattr",2);
}

void fun_backspace()
{
    putchar(\'\b\');
    putchar(\' \');
    putchar(\'\b\');
}

void fun_left()
{
    putchar(0x1b);
    putchar(0x5b);
    putchar(0x44);
}

void fun_right()
{
    putchar(0x1b);
    putchar(0x5b);
    putchar(0x43);
}