中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何自定義PHP的 printf 函數

發布時間:2020-07-30 10:37:25 來源:億速云 閱讀:207 作者:Leah 欄目:編程語言

如何自定義PHP的 printf 函數?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

 

注意

Libc 中關于 printf() 及其朋友的文檔位于此處。

你知道這些函數很有用,但有時無法提供足夠的功能。另外,你知道向 printf()添加格式字符串并非易事,沒有便攜性和有安全風險。

PHP 添加了自己的類似于 printf 的函數,取代了 libc 的,并且由內部開發者使用。他們主要添加新的格式,并使用 zend_string代替 char *等等,讓我們一起來看看。

警告

你必須掌握 libc 默認printf() 格式。請閱讀它們的文檔。

注意

添加了這些函數以 取代 libc 函數,意味著如果你使用了sprintf(),不會使用到 libc 的sprintf(),而是 PHP 取代了。除了傳統的 printf()外,其他內容均被替換。

傳統用途

首先,你不應該使用 sprintf(),因為該函數不執行任何檢查,并且導致許多緩沖區溢出錯誤。請避免使用它。

警告

盡可能避免使用 sprintf()

然后,你有一些選擇。

你知道結果緩沖區的大小

如果你知道緩沖區大小,snprintf() 或者 slprintf() 都可以使用。這些函數雖然在返回上不同,但是它們的功能是一樣的。

這兩個都是根據傳遞的格式來打印,并且無論發生什么,都會通過一個NUL 字節 ‘\0’來終止你的緩沖區。 但是,snprintf() 返回可以使用的字符數,而slprintf()返回可以有效使用的字符數,因此可以檢測過小的緩沖區和字符串截斷。這個不會計算最后的‘\0’

這里有個例子,以便你完全明白:

char foo[8]; /* 8字符大小的緩沖區 */
const char str[] = "Hello world"; /* 12個字符,包含 \0 */
int r;

r = snprintf(foo, sizeof(foo), "%s", str);
/* r = 11 ,即使這里只有7個可打印的字符可寫入 foo */

/* foo 的值現在是 'H' 'e' 'l' 'l' 'o' ' ' 'w' '\0' */

snprintf() 不是一個好用的函數,因為它不允許檢查最后的字符串截斷。就像上面例子你看到的,顯然“Hello world\0”不適合8字節的緩沖區,但是 snprintf() 仍然返回11給你,這是 strlen("Hello world\0") 的值。你沒有辦法檢查字符串被截斷了。

這是 slprintf()

char foo[8]; /* 8字符大的緩沖區 */
const char str[] = "Hello world"; /* 12個字符,包含 \0 */
int r;

r = slprintf(foo, sizeof(foo), "%s", str);
/* r = 7 ,因為7個可打印的字符被寫入 foo */

/* foo 現在的值是 'H' 'e' 'l' 'l' 'o' ' ' 'w' '\0' */

使用 slprintf(),結果緩沖區 foo 包含完全相同的字符串,但是如今返回值為7。7少于 “Hello world” 字符串的11個字符,所以你可以檢查它被截斷了:

if (slprintf(foo, sizeof(foo), "%s", str) < strlen(str)) {
    /* 發生字符串截斷 */
}

記住:

  • 這兩個函數總是以NUL終止字符串,不管是否截斷。最終的字符串是安全的 C 字符串。
  • 只有 slprintf()會檢查字符串截斷。

這兩個函數在 main/snprintf.c 中有詳細介紹。

你不知道緩沖區大小

現在如果你不知道結果緩沖區大小,則需要動態分配一個,并且使用spprintf()。記住,你必須自己釋放緩沖區。

這是例子:

#include <time.h>

char *result;
int r;

time_t timestamp = time(NULL);

r = spprintf(&result, 0, "Here is the date: %s", asctime(localtime(&timestamp)));

/* 現在結果類似:"Here is the date: Thu Jun 15 19:12:51 2017\n" */

efree(result);

spprintf() 返回被打印到結果緩沖區的字符數,不包括最后的‘\0’, 因此,你知道分配給你的字節數(減一)是多少。

請注意,是使用 ZendMM(請求分配)分配的,因此應作為請求的一部分使用,并使用 efree() 而不是free()釋放。

注意

Zend 內存管理章節 (ZendMM) 詳細介紹如何通過 PHP 分配動態內存。

如果你想要限制緩沖區大小,則將限制傳遞給第二個參數,如果你傳遞 0,意味著無限制:

#include <time.h>

char *result;
int r;

time_t timestamp = time(NULL);

/* 打印不超過 10 個字節 ||分配超過 11 個字節 */
r = spprintf(&result, 10, "Here is the date: %s", asctime(localtime(&timestamp)));

/* r == 10,并且給結果分配 11 個字節 */

efree(result);

注意

盡可能不要使用動態內存分配。這會影響執性能。如果有選擇,則選靜態堆棧分配緩沖區。

spprintf()寫在 main/spprintf.c 中。

那么 printf() 呢?

如果你需要 printf(),即打印格式化到輸出流,則使用php_printf()。該函數在內部使用 spprintf(),因此執行動態分配,以便將其發送到 SAPI 輸出(在 CLI 的情況下又稱為 stdout),或輸出緩沖區(CGI 緩沖區)后將其釋放,用于其他 SAPI。

特殊的 PHP printf 格式

記住,PHP 通過自己設計,取代了很多 libc 的 printf() 函數。你可以從閱讀源代碼中查看易于理解的參數解析 API。

這意味著解析算法的參數已完全被重寫,并且可能與你在 libc 使用的不同。即,在大多數情況下,不會關注 libc 環境。

可能會使用特殊的格式,就像 “%I64” 打印64位 int,或者“%I32”。你也可以使用 “%Z” 去打印 zval(根據 PHP 規則轉換為字符串),這是一個不錯的補充。

該格式化程序也認識無窮數,并打印 “INF”,或者將非數字打印為  “NAN”。

如果你錯誤的請求格式化程序打印一個 NULL 指針,libc 肯定會崩潰,而 PHP 會將 “(null)” 作為結果字符串返回。

注意

如果在打印中你看到神奇的 “(null)” 出現,意味著你將 NULL 指針傳遞給了 PHP printf 系列函數之一。

Printf() 到 zend_strings

zend_string 作為 PHP 源代碼里非常常見的結構,你可能需要 printf()zend_string,而不是傳統的 char *。為此,請使用strpprintf()

該 API 是 zend_string *strpprintf(size_t max_len, const char *format, ...) ,意味著返回zend_string 給你,而不是你期望的可打印字符數。不過你可以限制使用第一個參數來限制該數(傳遞 0 表示無窮大);并且你一定要記住將使用 Zend 內存管理分配 zend_string,并因此綁定當前請求。

顯然,該格式 API 與上面看到的共享。

這有個例子:

zend_string *result;

result = strpprintf(0, "You are using PHP %s", PHP_VERSION);

/* 對結果做些什么 */

zend_string_release(result);

關于 zend_ API 的注釋

您可能會遇到 zend_spprintf()zend_strpprintf() 函數。這些與上面看到的完全相同。

這只是 Zend 引擎和 PHP 核心之間分離的一部分,這個細節對我們并不重要,因為在源碼中,所有內容都是混合在一起的。

關于如何自定義PHP的 printf 函數問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

鹤庆县| 齐齐哈尔市| 正定县| 桦南县| 鄂伦春自治旗| 龙口市| 阿勒泰市| 遂宁市| 屯门区| 双辽市| 福鼎市| 井研县| 贵阳市| 阿勒泰市| 四子王旗| 汝阳县| 汉中市| 东乡族自治县| 临安市| 保靖县| 玉山县| 确山县| 庐江县| 朝阳县| 陕西省| 毕节市| 黄浦区| 温泉县| 饶平县| 旅游| 梁河县| 海盐县| 济源市| 浙江省| 甘谷县| 天长市| 望谟县| 安宁市| 图木舒克市| 兰西县| 玉山县|