簡體   English   中英

在不使用文件的情況下緩沖 C 中的 output

[英]Buffering output in C without using FILE

我正在嘗試使用 HTTP 標頭創建完整的 HTML 結果,並且內容長度的正確大小盡可能簡單。

我查看了 setbuf() function 但它僅適用於流,但我想直接將 output 緩沖到 memory 而不涉及磁盤。 是否有更簡單的 function 可以實現我想要實現的目標?

我知道在 php 中,可以使用 ob_start() 開始緩沖數據,並使用 ob_get_contents() 加載 memory 中的數據並確定大小,所以在 Z0D61F8370CAD1D412F70B84 中可以使用這樣的東西嗎?

int main(){
    char *mybuf=malloc(1048576); //Allocate 1MB memory for buffer
    char *mytitle="Webpage title";
    char *mydesc="This is a page version";
    int myver=2;
    //print HTTP header of known data
    printf("HTTP/1.1 200 OK\r\n");
    printf("Server: Something\r\n");
    printf("Content-type: text/html; charset=UTF-8\r\n");
    printf("Cache-control: no-cache\r\n");
    //Buffer HTML output to get size (we don't print these 4 lines yet)
    sprintf(mybuf,"<html><head><title>%s</title></head><body>\r\n",mytitle);
    sprintf(mybuf+strlen(mybuf),"<h1>%s</h1>\r\n",mytitle);
    sprintf(mybuf+strlen(mybuf),"<p>%s %d</p>\r\n",mydesc,myver);
    sprintf(mybuf+strlen(mybuf),"</body></html>\r\n");
    //Print content length header with size of buffered data
    printf("Content-length: %d\r\n",strlen(mybuf));
    //then print the rest of the data
    printf("%s",mybuf);
    free(mybuf);
    return 0;
}

C 語言規范沒有為僅由 memory 支持的流定義任何設施,沒有任何基礎文件。 您基於sprintf()的方法還不錯,但可以通過捕獲和使用返回值來改進它,返回值告訴您打印了多少個字符。 您可以使用它來避免所有strlen()調用,如果內容很大,這可能會節省大量資金,特別是如果它被打印成許多小塊:

size_t total_bytes_written = 0;
long rval;

rval = sprintf(mybuf, "<html><head><title>%s</title></head><body>\r\n", mytitle);
if (rval < 0) {
    // handle error ...
}
total_bytes_written += rval;

rval = sprintf(mybuf + total_bytes_written, "<h1>%s</h1>\r\n", mytitle);
// ...

或者,如果您使用的是符合 POSIX.1-2008 或更高版本的系統,並且您願意將您的程序限制在此類系統中,那么您可能會感興趣open_memstream() 這為您提供了由 memory 的動態管理塊支持的 stream。 您可以使用fprintf()或其他 stream IO 函數寫入它,完成后,讀回指向數據和總大小的指針。 像這樣的東西:

char *buf_ptr = NULL;
size_t buf_size = 0;
FILE *mem = open_memstream(&buf_ptr, &buf_size);

fprintf(mem, "<html><head><title>%s</title></head><body>\r\n", mytitle);
fprintf(mem, "<h1>%s</h1>\r\n", mytitle);

// ...

fclose(mem);  // mandatory
printf("Content-length: %d\r\n", buf_size);
printf("%s", buf_ptr);  // the data are guaranteed null-terminated
free(buf);  // you are responsible for this

(為簡潔起見,省略了所有錯誤檢查。)

這使您無需手動跟蹤大小,並為您提供動態增長的后備緩沖區,以便您可以以合理的 memory 效率處理大小輸出。

您可能知道,Content-Length 是您身體的大小。 這意味着您必須至少知道主體的長度,然后才能生成 header(或使用分塊傳輸)。 sprintf 返回寫入的字節數,或者累積這些字節數,或者將其壓縮到單個調用中:

int n = snprintf(mybuf,
  mybuf_len,
  "<html><head><title>%s</title></head><body>\r\n"
  ...
  "</body></html>\r\n",
  mytitle, ..., myver
);

然后用你想出的 n 生成 header(n) 。 如果您傳入 NULL 而不是 mybuf,則無需寫入任何內容即可獲得大小,然后您可以使用第一次調用獲得的計數在 header 中再寫入一次。 如果您願意,可以將其包裝在 function 或兩個中:

int response(const char *header, int body_len, const char body[body_len]) {
   ...
}

考慮選擇一個模板庫(我喜歡 mustache 格式)。 使用原始字符串手動執行此操作有點混亂和錯誤修剪。 你在那里分配了一個 1 MB 的緩沖區,但使用起來像 a.5k,無論如何你總有一天會在腳上開槍。

這個問題有多種解決方案:

  • 你的方法沒問題,但你應該使用snprintf()來避免潛在的緩沖區溢出:

     int main() { char *mybuf = malloc(1048576); //Allocate 1MB memory for buffer const char *mytitle = "Webpage title"; const char *mydesc = "This is a page version"; int myver = 2; size_t pos = 0; //print HTTP header of known data printf("HTTP/1.1 200 OK\r\n"); printf("Server: Something\r\n"); printf("Content-type: text/html; charset=UTF-8\r\n"); printf("Cache-control: no-cache\r\n"); //Buffer HTML output to get size (we don't print these 4 lines yet) pos += snprintf(mybuf + pos, pos < sizeof mybuf? sizeof mybuf - pos: 0, "<html><head><title>%s</title></head><body>\r\n", mytitle); pos += snprintf(mybuf + pos, pos < sizeof mybuf? sizeof mybuf - pos: 0, "<h1>%s</h1>\r\n", mytitle); pos += snprintf(mybuf + pos, pos < sizeof mybuf? sizeof mybuf - pos: 0, "<p>%s %d</p>\r\n", mydesc, myver); pos += snprintf(mybuf + pos, pos < sizeof mybuf? sizeof mybuf - pos: 0, "</body></html>\r\n"); //Print content length header with size of buffered data printf("Content-length: %zu\r\n", pos); printf("\r\n"); //then print the rest of the data printf("%s", mybuf); free(mybuf); return 0; }
  • 這是一個更簡單的方法,結合調用並使用snprintf計算長度:

     int main() { const char *mytitle = "Webpage title"; const char *mydesc = "This is a page version"; int myver = 2; size_t length = 0; //print HTTP header of known data printf("HTTP/1.1 200 OK\r\n"); printf("Server: Something\r\n"); printf("Content-type: text/html; charset=UTF-8\r\n"); printf("Cache-control: no-cache\r\n"); //Buffer HTML output to get size (we don't print these 4 lines yet) length = snprintf(NULL, 0, "<html><head><title>%s</title></head><body>\r\n" "<h1>%s</h1>\r\n" "<p>%s %d</p>\r\n" "</body></html>\r\n", mytitle, mytitle, mydesc, myver); //Print content length header with size of buffered data printf("Content-length: %zu\r\n", length); printf("\r\n"); //then print the rest of the data printf("<html><head><title>%s</title></head><body>\r\n" "<h1>%s</h1>\r\n" "<p>%s %d</p>\r\n" "</body></html>\r\n", mytitle, mytitle, mydesc, myver); return 0; }
  • 您可以按照John Bollinger的解釋使用open_memstream()

     int main() { const char *mytitle = "Webpage title"; const char *mydesc = "This is a page version"; int myver = 2; size_t length = 0; char *buf_ptr = NULL; size_t buf_size = 0; // the buffer size int length = 0; // the length of the output FILE *mem = open_memstream(&buf_ptr, &buf_size); //print HTTP header of known data printf("HTTP/1.1 200 OK\r\n"); printf("Server: Something\r\n"); printf("Content-type: text/html; charset=UTF-8\r\n"); printf("Cache-control: no-cache\r\n"); //Buffer HTML output to get size (we don't print these 4 lines yet) if (mem == NULL) { // Deal with the error printf("Internal error\r\n"); return 1; } length = fprintf(mem, "<html><head><title>%s</title></head><body>\r\n" "<h1>%s</h1>\r\n" "<p>%s %d</p>\r\n" "</body></html>\r\n", mytitle, mytitle, mydesc, myver); //Print content length header with size of buffered data printf("Content-length: %zu\r\n", length); printf("\r\n"); //then print the rest of the data //beware that buf_ptr is not necessarily null terminated printf("%.*s", length, buf_ptr); //could also use fwrite(f, buf_ptr, 1, length, stdout); free(buf_ptr); return 0; }
  • 您可以實現自己的mem_printf()分配或重新分配 memory 緩沖區:

     #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int mem_printf(char **bufp, size_t *lenp, const char *fmt, ...) { char buf[1024]; va_list ap; char *newbuf; int len; va_start(ap, fmt); len = vsnprintf(buf, sizeof buf, fmt, ap); va_end(ap); if (len < 0) return -1; newbuf = realloc(*bufp, *lenp + len + 1); if (newbuf == NULL) return -1; *bufp = newbuf; if ((size_t)len < sizeof buf) { memcpy(newbuf + *lenp, buf, len + 1); } else { va_start(ap, fmt); vsnprintf(newbuf + *lenp, len + 1, fmt, ap); va_end(ap); } *lenp += len; return len; } int main() { const char *mytitle = "Webpage title"; const char *mydesc = "This is a page version"; int myver = 2; char *buf_ptr = NULL; size_t buf_len = 0; //print HTTP header of known data printf("HTTP/1.1 200 OK\r\n"); printf("Server: Something\r\n"); printf("Content-type: text/html; charset=UTF-8\r\n"); printf("Cache-control: no-cache\r\n"); //Buffer HTML output to get size (we don't print these 4 lines yet) mem_printf(&buf_ptr, &buf_len, "<html><head><title>%s</title></head><body>\r\n" "<h1>%s</h1>\r\n" "<p>%s %d</p>\r\n" "</body></html>\r\n", mytitle, mytitle, mydesc, myver); //Print content length header with size of buffered data printf("Content-length: %zu\r\n", buf_len); printf("\r\n"); //then print the rest of the data fputs(buf_ptr, stdout); free(buf_ptr); return 0; }

暫無
暫無

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

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