簡體   English   中英

打印行號隨 C 中的每個輸入遞增

[英]Print line number incrementing with each input in C

當它要求輸入時,我想在控制台中看到行號。

就像是:

1 foo
2 bar
3 baz

這里 1 顯示在提示中,我輸入任何輸入並按 Enter 然后顯示下一個數字,我也這樣做。

對於初學者來說,在 C 中有什么簡單的幫助嗎?

這可能會幫助你。

#define _CRT_SECURE_NO_WARNINGS

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

int main() {
    int lineNo = 0;
    char name[256];

    do {
        printf("%d ", ++lineNo);
        scanf("%s", name);

    } while (strlen(name) > 0);

    return 0;
}

這是一個廣泛的問題,涉及兩個重點,算法和 C 實現的細節,因此我將簡要介紹這兩點。

算法

作為手動執行此操作的人,可以通過以下方式進行操作:

  1. 保持輸入的計數器,最初為零
  2. 增加計數器
  3. 打印計數器
  4. 征求意見
  5. 回到第 2 步

這將無限重復,直到用戶中止。 或者我們可以選擇任意數量的輸入,比如五個。 然后在返回第 2 步之前,程序將檢查計數器是否達到限制。

由於 Python 讀起來很像偽代碼,我們可以將它用於我們的草稿算法:

#!/usr/bin/env python3

input_count = 0
max_count = 5

while input_count < max_count:
    input_count += 1
    line = input("%d: " % input_count)
    # do something with line, e.g.:
    print(line)

我在這里使用了printf樣式,但我通常更喜歡較新的格式化字符串文字 但是關於 Python 已經足夠了。

C 實現

用 C 實現這個簡單的程序可能會稍微復雜一些,因為 C 是一種相對低級的語言。 與 Python 版本不同,C 要求保存字符串的變量具有特定的大小,除非我們希望進行動態內存分配,我們在這里不會這樣做,但稍后會詳細介紹。 一個簡單的版本可能如下所示:

#include <stdio.h>
#define MAX_LINE 120

void main(void)
{
        char line[MAX_LINE];
        int input_count = 0;
        const int max_count = 5;

        while (input_count++ < max_count) {
                printf("%d: ", input_count);
                if (fgets(line, sizeof(line), stdin) != NULL) {
                        /* do something with line, e.g.:
                        printf("%s", line);
                        */
                }
        }
}

無限線

這很好,但是如果我們不知道用戶想要輸入多少行怎么辦? 當他們在一個空行上按 Enter 時,我們可以停止程序,而不是固定的次數,比如 5。對上面的稍作修改讓我們做到這一點:

#include <stdio.h>
#include <stdbool.h>

#define MAX_LINE 120


void main(void)
{
        char line[MAX_LINE];
        int input_count = 1;
        bool done = false;

        do {
                printf("%d: ", input_count++);
                if (fgets(line, sizeof(line), stdin) != NULL) {
                        /* do something with line, e.g.:
                        printf("%s", line);
                        */
                }
                if (line[0] == '\n') {
                        done = true;
                }
        } while (!done);
}

溢出問題

到目前為止,我們假設我們的用戶會表現得總是輸入比緩沖區允許的大小更少的字符,包括他們按 Enter 時的換行符,以及空終止符字節。 但是一個好的程序也會處理錯誤和替代方案。

為了更容易地看到溢出行為,讓我們將緩沖區減少到 12,這樣如果字符串除終止符之外還包含換行符,則它只允許 10 個可見字符。 它還打印行緩沖區中的值,因此我們可以看到結果:

#include <stdio.h>
#include <stdbool.h>

#define MAX_LINE 12


void main(void)
{
        char line[MAX_LINE];
        int input_count = 1;
        bool done = false;

        do {
                printf("%d: ", input_count++);
                if (fgets(line, sizeof(line), stdin) != NULL) {
                        printf("%s\n", line);
                }
                if (line[0] == '\n') {
                        done = true;
                }
        } while (!done);
}

這是一個示例會話:

1: 123456789ABCDEF
123456789AB
2: CDEF

3:

我在這里所做的只是輸入 16 個字符: 123456789ABCDEF ,然后按 Enter。 但是我們可以在上面看到計算了第二個輸入,它具有CDEF 然后我在提示 3 處按 Enter 鍵,程序結束。

這里發生了什么? 由於超出了緩沖區的大小, fgets()停止讀取字符並存儲剛好足以填充緩沖區的字符,包括空終止符。 所以這是 11 個可見字符,或123456789AB 然后在下一次循環迭代中打印輸入計數 2。然后再次調用fgets() 但是,因為標准輸入流中有未讀字符, fgets()將首先讀取這些字符,直到遇到換行符,因為它們現在完全適合緩沖區。 該行在我有機會輸入任何內容之前被打印出來,並且循環移動到輸入編號 3,因為換行符表示輸入 2 的結束。

這是不可取的。 截斷是意料之中的,它是fgets()的一個特性,使它可以安全使用,不像scanf()gets() ,它們允許在運行時危險的緩沖區溢出。 但缺點是第二個輸入現在因第一個輸入的 stdin溢出而“損壞”。

有些人可能會嘗試使用fflush來丟棄不需要的輸入,但這並不適用於終端,並且具有未定義或無效的效果。

由於fgets()不夠禮貌,無法返回一個值來告訴調用者發生了溢出,因此我們將使用替代解決方案進行輸入,使用getchar() ,這將給我們更大的控制權。

我們將從一個初始版本開始,使用一個名為input()的函數,它可以用作fgets()替代品,除非它不需要第三個參數,因為我們將始終使用 stdin:

#include <stdio.h>
#include <stdbool.h>

#define MAX_LINE 12

char *input(char *line, size_t buffer_len)
{
        size_t len = 0;
        bool done = false;
        int keychar;

        while (!done && len < buffer_len && (keychar = getchar()) != EOF) {
                line[len++] = keychar;
                if (keychar == '\n' || len == buffer_len - 1) {
                        done = true;
                }
        }
        if (len < buffer_len) {
                line[len] = '\0';
                return line;
        }
        /* Error, buffer too small */
        return NULL;
}

void main(void)
{
        char line[MAX_LINE] = "";
        int input_count = 1;
        bool done = false;

        do {
                printf("%d: ", input_count++);
                if (input(line, sizeof(line)) != NULL) {
                        printf("%s\n", line);
                } else {
                        printf("Error reading input\n");
                        break;
                }
        } while (line[0] != '\n');
}

運行這個程序將顯示與前面提到的fgets()相同的溢出問題,但是現在我們可以控制了,我們可以將其更改為這個最終版本:

#include <stdio.h>
#include <stdbool.h>

#define MAX_LINE 12

bool input(char *line, size_t buffer_len)
{
        size_t len = 0;
        bool done = false;
        int keychar;
        bool truncated = false;
        bool buffer_error = true;

        while (!done && len < buffer_len && (keychar = getchar()) != EOF) {
                line[len++] = keychar;
                if (keychar == '\n' || len == buffer_len - 1) {
                        done = true;
                }
        }
        if (keychar != '\n') {
                truncated = true;
                /* consume truncated chars */
                while ((keychar = getchar()) != '\n') {
                        if (keychar == EOF)
                                break;
                }
        }
        if (len < buffer_len) {
                line[len - 1] = '\n';   /* for consistency */
                line[len] = '\0';
                buffer_error = false;
        }

        return !truncated && !buffer_error;
}

void main(void)
{
        char line[MAX_LINE] = "";
        int input_count = 1;
        bool done = false;

        do {
                printf("%d: ", input_count++);
                if (input(line, sizeof(line))) {
                        printf("%s\n", line);
                } else {
                        /* Ignoring the buffer error scenario */
                        printf("Input truncated: %s\n", line);
                }
        } while (line[0] != '\n');

        printf("Goodbye!\n");
}

現在讓我們嘗試使用溢出:

1: 123456789ABCDEF
Input truncated: 123456789A

2: 123456789A
123456789A

3: It works!
It works!

4:


Goodbye!

如我們所見,現在檢測到截斷,並且不會干擾后續輸入。 當然,我們仍然可以使用fgets()並且在每次調用之前始終注意消耗不需要的輸入。 我只是想演示fgets()getchar( ) 的用法,它們是字符串控制台輸入的兩個最重要的標准函數。

替代字符串輸入解決方案

還有一些其他的鍵盤字符串輸入解決方案,它們是非標准的,因此不太便攜。 有些人可能在conio.h頭文件中遇到過getchgetche ,它們是在 MS-DOS 時代由 Microsoft 或 Borland 引入的。 我相信它也得到了 Turbo C/C++ 的支持,Turbo C/C++ 是當時 Borland 流行的 IDE 和編譯器。 有一個關於如何在 Linux 上獲取它問題

微軟仍然為沒有回顯的輸入提供_getch() ,這可以讓你做很酷的事情,以及可以回顯字符的配套_getche()函數,很像getchar() 它們還有一個安全版本的scanf ,它允許指定緩沖區大小,就像我們對fgets所做的那樣,稱為scanf_s

為了避免完全處理截斷,GNU 添加了現在在 POSIX.1-2008 中標准化的getline() scanf還有一個擴展,它包括一個可選的'm'字符,它動態分配一個足夠大的緩沖區來保存輸入字符串。 最后,還有readline ,一個花哨的,有些人可能會說很重的 GNU 擴展,它提供了具有編輯功能的提示。 在 Linux 環境中,這些都是有效的選項。

結論

如果這個“簡短”的答案看起來太長,那是因為關於 C 中的輸入和輸出有很多要說的。我在這里幾乎沒有觸及表面。 那里有很多好書,其中涵蓋了更詳細的內容。

對於那些剛開始使用 C 的人,我建議在玩的時候堅持使用fgets()getchar( )。 這比習慣於使用不安全的函數(如scanfgets讀取字符串要好。 當需要進行認真的編碼時,您可以選擇使用非標准和特定於平台的解決方案或實現自己的解決方案。

我想我為此嘗試了我所知道的最簡單的方法。 您可以在二維數組的幫助下完成此操作。 以下是這方面的代碼:

#include<stdio.h>
main()
{
char arr[3][1];
int i,j;
for(i=0;i<3;i++){
    for(j=0;j<1;j++){
        printf("%d ",i+1);
        scanf("%s",&arr[i][j]);
        }
}

}

您可以根據所需的輸入數量更改數組的大小。 此代碼需要 3 個輸入,您可以根據輸入需要將其更改為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM