簡體   English   中英

在 C 中動態指定 scanf 的最大字符串長度(如 printf 中的“%*s”)

[英]Specifying the maximum string length to scanf dynamically in C (like "%*s" in printf)

我可以使用此技術指定scanf讀取到buffer的最大字符數:

char buffer[64];

/* Read one line of text to buffer. */
scanf("%63[^\n]", buffer);

但是如果我們在編寫代碼時不知道緩沖區長度怎么辦? 如果它是函數的參數呢?

void function(FILE *file, size_t n, char buffer[n])
{
    /* ... */
    fscanf(file, "%[^\n]", buffer); /* WHAT NOW? */
}

此代碼容易受到緩沖區溢出的影響,因為fscanf不知道緩沖區有多大。

記得之前看到過這個,開始認為是解決問題的方法:

fscanf(file, "%*[^\n]", n, buffer);

我首先想到的是, *"%*[*^\\n]"指的是最大字符串大小傳遞參數(在這種情況下n )。 這就是printf *的含義。

當我檢查scanf的文檔時,我發現這意味着scanf應該丟棄[^\\n]

這讓我有些失望,因為我認為能夠為scanf動態傳遞緩沖區大小將是一個非常有用的功能。

有什么方法可以將緩沖區大小動態傳遞給scanf嗎?

基本答案

scanf()沒有printf()格式說明符*的模擬。

The Practice of Programming 中,Kernighan 和 Pike 推薦使用snprintf()創建格式字符串:

size_t sz = 64;
char format[32];
snprintf(format, sizeof(format), "%%%zus", sz);
if (scanf(format, buffer) != 1) { …oops… }

額外的信息

將示例升級為完整功能:

int read_name(FILE *fp, char *buffer, size_t bufsiz)
{
    char format[16];
    snprintf(format, sizeof(format), "%%%zus", bufsiz - 1);
    return fscanf(fp, format, buffer);
}

這強調了格式規范中的大小比緩沖區的大小小一(它是可以存儲的非空字符數,不計算終止空字符)。 請注意,這與fgets()形成對比,其中大小(一個int ,順便說一句;不是size_t )是緩沖區的大小,而不是少一個。 有多種改進功能的方法,但它表明了這一點。 (如果這是你想要的,你可以用[^\\n]替換格式中的s 。)

此外,正如Tim Čas評論中指出的那樣,如果您想要(其余的)一行輸入,通常最好使用fgets()來讀取該行,但請記住,它在其輸出中包含換行符(而%63[^\\n]將換行符留給下一個 I/O 操作讀取)。 對於更一般的掃描(例如,2 或 3 個字符串),這種技術可能更好——特別是如果與fgets()getline() ,然后使用sscanf()來解析輸入。

此外,由 Microsoft(或多或少)實施並在 ISO/IEC 9899-2011(C11 標准)的附件 K 中標准化的 TR 24731-1“安全”功能明確要求長度:

if (scanf_s("%[^\n]", buffer, sizeof(buffer)) != 1)
    ...oops...

這避免了緩沖區溢出,但如果輸入太長可能會產生錯誤。 可以/應該像以前一樣在格式字符串中指定大小:

if (scanf_s("%63[^\n]", buffer, sizeof(buffer)) != 1)
    ...oops...

if (scanf_s(format, buffer, sizeof(buffer)) != 1)
    ...oops...

請注意,對於使用生成的格式字符串的代碼,必須忽略或抑制有關“非常量格式字符串”的警告(來自某些編譯器的某些標志集)。

scanf系列函數中確實沒有可變寬度說明符。 替代方法包括動態創建格式字符串(盡管如果寬度是編譯時常量,這似乎有點愚蠢)或簡單地接受幻數。 一種可能性是使用預處理器宏來指定緩沖區和格式字符串寬度:

#define STR_VALUE(x) STR(x)
#define STR(x) #x

#define MAX_LEN 63

char buffer[MAX_LEN + 1];
fscanf(file, "%" STR_VALUE(MAX_LEN) "[^\n]", buffer);

另一種選擇是#define字符串的長度:

#define STRING_MAX_LENGTH "%10s"

或者

#define DOUBLE_LENGTH "%5f"

暫無
暫無

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

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