5

したがって、基本的にユーザー入力の文字を読み取り、「q」が入力されるまでそれらを出力する次のコードがあります。

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

int main(void) {
    char c; 
    static struct termios oldtio, newtio;
    tcgetattr(0, &oldtio);
    newtio = oldtio;
    newtio.c_lflag &= ~ICANON;
    newtio.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &newtio);

    printf("Give text: ");
    fflush(stdout);
    while (1) {
        read(0, &c, 1);
        printf("%c", c);
        fflush(stdout);
        if (c == 'q') { break; }
    }
    printf("\n"); 
    tcsetattr(0, TCSANOW, &oldtio);

    return 0;
}

メイン関数の冒頭で、正規モードをオフにして、ユーザーが自分の入力を表示できるようにします。また、エコーをオフにして、たとえば上矢印キーを押したときに「^[[A」のようなものがポップアップしないようにします。これは機能しますが、端末ウィンドウの上部の行にカーソルを移動することもできますが、これは良くありません。ユーザーが現在の行内でのみ移動できるように、これを修正する方法はありますか?

別の問題は、バックスペースです。それを押すと、プログラムはカーソルの現在の位置に残っている文字を消去する代わりに、奇妙な記号 (0x7f と思われます) を出力します。プログラムで何らかの方法でバックスペース キーの出力を適切に処理する必要がありますが、この奇妙な 16 進数であるため、その方法がわかりません。これに関するヒントはありますか?

これを機能させるために私が考えていたオプションの 1 つは、正規モードを使用して、矢印キーとバックスペース機能が自動的に使用されるようにすることです。ただし、標準モードは行ごとに機能するため、ユーザーが「Enter」を押すまでテキストは表示されません。これまでのところ、入力中にユーザーに自分の入力を表示させる方法はわかりませんでした。これは可能ですか?

そして、ncurses や readline の提案はしないでください。termios.h を使用してこれを行いたいです。

4

2 に答える 2

5

manページを調べましたか?(あるべきか、オンラインman termiosのどこかを見てください)

ECHOEそこで、次の効果があると言われている旗を見つけました。

ICANON も設定されている場合、ERASE 文字は前の入力文字を消去し、WERASE は前の単語を消去します。

これでバックスペースの問題は解決しますか?

また、マニュアルページの例をご覧になることをお勧めします。たとえば、次のことができます。

newtio.c_lflag &= ~(ECHO | ECHOE | ICANON);

...一度に複数のフラグを 1 行で設定します。マニュアルページは初心者にとって読みにくいことは承知していますが、慣れて使えば使うほど、C/POSIX 関数などを検索するのに効率的になります (念のため、それらを使用しないでください)。とりあえず)。

矢印キーの問題: -関数を試すことができるかもしれませんcfmakeraw()。その説明は有望に聞こえます。矢印キーについてこれ以上調べる時間がありませんでした。ただし、man ページで別の便利な情報を見つけることができるかもしれません。

ところで:termios面白そうに見えますが、特定のコマンド ライン プログラムがどの機能を使用しているか常に疑問に思っていました。あなたの質問で何かを学びました、ありがとう!

編集

今週末、さらに調査を行ってきました。バックスペース キーを押したときに出力される「奇妙な」記号は、非常に簡単に隠すことができます。これは ASCII 値0x7fです。したがって、単純な追加

if (c == 0x7f) { continue; }

...バックスペースキーを無視するだけです。または、最後の文字を削除する方法で処理します (以下のコード例を参照)。

矢印キーは ASCII 文字ではないため、この単純な回避策は機能しません :/ ただし、トピック 1トピック 2の 2 つのトピックは、この問題の処理にも役立ちました。基本的に、矢印キーを押すと、一連の文字が送信されstdinます (詳細については、2 番目のリンクを参照してください)。

あなたが望むように(私が思うに)動作する私の完全なコードは次のとおりです。

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

int main(void) {
    char c;
    static struct termios oldtio, newtio;
    tcgetattr(0, &oldtio);
    newtio = oldtio;
    newtio.c_lflag &= ~ICANON;
    newtio.c_lflag &= ~ECHO;
    tcsetattr(0, TCSANOW, &newtio);

    printf("Give text:\n");
    fflush(stdout);
    while (1) {
        c = getchar();

        // is this an escape sequence?
        if (c == 27) {
            // "throw away" next two characters which specify escape sequence
            c = getchar();
            c = getchar();
            continue;
        }

        // if backspace
        if (c == 0x7f) {
            // go one char left
            printf("\b");
            // overwrite the char with whitespace
            printf(" ");
            // go back to "now removed char position"
            printf("\b");
            continue;
        }

        if (c == 'q') {
            break;
        }
        printf("%c", c);
    }
    printf("\n");
    tcsetattr(0, TCSANOW, &oldtio);

    return 0;
}

ところで、次のコードで完全なエスケープ シーケンスを取得できます。

int main(void) {
    char c;
    while (1) {
        c = getchar();
        printf("%d", c);
    }
    return 0;
}

この完全なものは非常に汚いハックであり、いくつかの特別なキーの処理を忘れがちであると言う必要はないと思います. たとえば、私のコードでは、page-up/downまたはhomeキーを処理していません... --> 上記のコードは完全にはほど遠いですが、開始点を示しています。またterminfo、必要な多くの情報を提供できるものも確認する必要があります。また、より移植性の高いソリューションにも役立つはずです。ご覧のとおり、この「単純な」ことは非常に複雑になる可能性があります。

于 2014-10-28T18:07:19.917 に答える
0

実際には、矢印キーを処理するには、適切なサイズの ncurses のチャンクを実装する必要があります。長所と短所があります。コマンドライン アプリケーションで ncurses を使用することの主な欠点は、通常は画面がクリアされることです。ただし、(n)curses は関数filterを提供します。ncurses ソースにサンプル プログラム「test/filter.c」があり、左矢印キーを消去文字として使用してこれを示し、結果の行を system() に渡して簡単なコマンドを実行します。サンプルは 100 行未満のコードで、上記の例よりも単純で完全なようです。

于 2015-02-05T01:05:06.717 に答える