1. 程式人生 > >Linux下程式輸入輸出(^H、^C問題)

Linux下程式輸入輸出(^H、^C問題)

問題:

       1、在linux的遠端終端上執行程式出現個人開發的應用按backspace鍵時有^H等不識別字符;

       2、在CentOS7下的應用程式按backspace可以刪除但是delete鍵就無法實現像windows下的刪除後側的字元;

       3、在linux下的應用如果要使用類似shell實現自己應用的歷史記錄時,方向鍵無法識別,出現:^[[A、^[[B等字元,Tab鍵依舊沒有辦法識別;

       4、輸入回車時列印很多空行,不會有任何提示或者類似linux 的終端列印;

解決方法:

1、設定遠端工具的引數,以SecureCRT為例:

       1.1、 依次選擇“選項”

->“會話選項”->彈出如下圖片進行選擇:

                          

                                                       圖1.SecureCRT選擇對映鍵

   其他版本的SecurtCRT查詢相似的選項。

1.2、設定SecurtCRT的終端模式:

       Linux支援Linux、VT100、VT200等終端模式,Linux舊的終端模式需要Ctrl+Backspace才能刪除字元,因此後期推出了VT100的終端模式,但是VT100中字元編碼混亂以至於Delete和BackSpace鍵不能嚴格區分,因此Linux的某些程式碼中含有註釋:magic shit

        ^H出現時,切換SecurtCRT的終端模式實現鍵值對映,如下:

                       

                                          圖2.選擇SecurtCRT連線Linux的終端模式

一般選擇VT100,此時的BackSpace鍵和Delete鍵需要對映一致否則Linux在使用者寫的命令列模式下沒有辦法識別Delete鍵。

2、在Linux的應用程式中實現ReadLine和EditLine,使應用程式執行時不會出現鍵值無法識別的情況

原始碼參考:

程式碼實現總體思路:

i.首先解除現有Linux終端的引數設定,使終端引數迴歸到最簡單的模式;

ii.其次,通過標準輸入輸出裝置讀取字元流和重新整理字元流,從而實現scanf和printf兩個函式;

iii.在讀寫標準裝置完成後,恢復終端原有的模式,使其他依賴當前終端的應用程式不會跑飛或出現異常。

2.1、終端設定標頭檔案: 

#include <termios.h> #include <unistd.h>

相關結構體:

      struct termios

           {

                     tcflag_t c_iflag;      /* input modes 輸入模式 */                      tcflag_t c_oflag;      /* output modes  輸出模式 */                      tcflag_t c_cflag;      /* control modes 控制模式*/                      tcflag_t c_lflag;      /* local modes  本地模式 */                      cc_t     c_ccNCCS ] ;   /* special characters 特殊字元控制 * /

            };

結構體各引數取值描述可以通過在Linux下查詢對應的函式,如:man tcgetattr

2.2、讀取標準輸入裝置的字元

char input;

read(STDIN_FILENO,&input,1);

2.3、向標準輸出裝置寫入字元重新整理到螢幕上

char output[100];

write(STDOUT_FILENO,output,strlen(output));

2.4、改造後的Readline和Editline的實現

分三個檔案:read_line.c、read_line.h及main.c

a.read_line.c檔案內容如下:

/*********************************************************************
 * 檔名:read_line.c
 * 描述: C語言實現的簡易readline
 * 時間: 2018-11-11
 * 建立: Firdin
 * 郵箱: [email protected]
 * 
 * 修改歷史:
 *   版本        時間           作者           內容
 -----------------------------------------------------
 *  V0.1      2018-11-11       firdin       從github上移植,編碼:UTF-8
 *
 *********************************************************************/

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

#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <sys/ioctl.h>
#include <unistd.h>
#include "read_line.h"

b.read_line.h檔案內容如下:

#ifndef _READ_LINE_H
#define _READ_LINE_H

#define READLINE_DEFAULT_HISTORY_MAX_LEN 		100
#define READLINE_MAX_READLINE_BUFFER_SIZE 		4096

/* readline依賴的資料結構體 */
typedef struct
{
    int ifd;            /* 當前終端標準輸入檔案描述符 */
    int ofd;            /* 當前終端標準輸出檔案描述符 */
    char *buf;          /* 行buffer指標 */
    size_t buflen;      /* 行buffer長度 */
    const char *prompt; /* 字首顯示內容buffer指標 */
    size_t plen;        /* 字首長度 */
    size_t pos;         /* 當前游標位置 */
    size_t oldpos;      /* 重新整理前游標位置 */
    size_t len;         /* 正在編輯的行長度 */
    size_t cols;        /* 終端列數 */
    size_t maxrows;     /* 多行顯示時,記錄當前剩餘行數 */
    int history_index;  /* 命令列歷史記錄指標 */
}readline_db_t;

/* 鍵值表,可以通過鍵值工具獲取想要新增的組合鍵的鍵值 */
enum key_value
{
	KEY_NULL = 0,	    /* NULL */
	CTRL_A = 1,         /* Ctrl+a */
	CTRL_B = 2,         /* Ctrl-b */
	CTRL_C = 3,         /* Ctrl-c */
	CTRL_D = 4,         /* Ctrl-d */
	CTRL_E = 5,         /* Ctrl-e */
	CTRL_F = 6,         /* Ctrl-f */
	CTRL_H = 8,         /* Ctrl-h */
	TAB = 9,            /* Tab */
	CTRL_K = 11,        /* Ctrl+k */
	CTRL_L = 12,        /* Ctrl+l */
	ENTER = 13,         /* Enter */
	CTRL_N = 14,        /* Ctrl-n */
	CTRL_P = 16,        /* Ctrl-p */
	CTRL_T = 20,        /* Ctrl-t */
	CTRL_U = 21,        /* Ctrl+u */
	CTRL_W = 23,        /* Ctrl+w */
	ESC = 27,           /* Escape */
	BACKSPACE =  127    /* Backspace */
};

#endif /* End of _READ_LINE_H */

c.main.c示例應用檔案內容如下:

以上程式碼遵循GNU開源精神,如有不適處請多加指正,多謝。

待補充完善