php的二进制安全
php的二进制安全
本文主要从三个角度来阐述php的二进制安全:1. 什么叫php的二进制安全;2. 什么结构确保了php的二进制安全;3. 这种结构还有哪些其它方面的应用?
做到知其然,也知其所以然。
一句话解释:
php的内部函数在操作二进制数据时能保证达到预期的结果,例如str_replace、stristr、strcmp等函数,我们就说这些函数是二进制安全的。
举个列子:
我们来对比一下C和php下的strcmp函数。
C代码如下
main(){ char ab[] = "aa\0b"; char ac[] = "aa\0c"; printf("%d\n", strcmp(ab, ac)); printf("%d\n", strlen(ab));  }
结果:
0
2
解读:
也就是说C语言认为ab和ac这两个字符串是相等的,而且ab的长度为2.
php代码如下
<?php $ab = "aa\0b"; $ac = "aa\0c"; var_dump(strcmp($ab, $ac)); var_dump(strlen($ab));  ?>
int(-1)
int(4)
解读:
也就是php语言认为ab和ac这两个字符串是相等的,而且ab的长度为4。
聪明的你,应该已经发现问题在哪了吧,不错,对于c语言‘\0’是字符串的结束符,所以在C语言中对于字符串“aa\0b”,它读到'\0'就会默认字符读取已经结束,而抛掉后面的字符串'b',导致我们看到strlen(“aa\0b”)的值为2
那问题又来了,php都是C来开发的,为什么php做到了二进制安全呢?
先来看看php的变量存储zval结构
php会根据type的值来决定访问value的哪个成员,为字符串时,我们会访问红框标识的str结构,这便是底层字符串的存储结构,它有两个值,一个是指向字符串的指针val,另一个是记录字符串长度的len值,就是因为有len这个值,导致了php是二进制安全的:因为它不需要像C一样通过是否遇到'\0'结尾符来判断整个字符串是否读取完毕,而是通过len这个值指定的长度进行读取。
可见一个小小的数据结构改进,为我们带来了更多想象的空间,可谓是结构的一小步,功能的一大步。
拓展:
这么好用的结构,显然会被到处使用,我们常用的redis,在底层存储数据时就用到了这种结构,Redis 没有直接使用 C 语言传统的字符串表示(以空字符结尾的字符数组),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将 SDS 用作 Redis 的默认字符串表示
看下SDS的结构定义
struct sdshdr { // 记录 buf 数组中已使用字节的数量 // 等于 SDS 所保存字符串的长度 int len; // 记录 buf 数组中未使用字节的数量 int free; // 字节数组,用于保存字符串 char buf[]; };
以下内容足以阐明这一点:(摘自http://redisbook.com/preview/sds/different_between_sds_and_c_string.html#id6)
C 字符串中的字符必须符合某种编码(比如 ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾 ——这些限制使得 C 字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。
举个例子,如果有一种使用空字符来分割多个单词的特殊数据格式,如图 2-17 所示,那么这种格式就不能使用 C 字符串来保存,因为 C 字符串所用的函数只会识别出其中的
"Redis"
,而忽略之后的
"Cluster"
。
虽然数据库一般用于保存文本数据,但使用数据库来保存二进制数据的场景也不少见,因此,为了确保 Redis 可以适用于各种不同的使用场景,SDS 的 API 都是二进制安全的(binary-safe):所有 SDS API 都会以处理二进制的方式来处理 SDS 存放在
buf
数组里的数据,程序不会对其中的数据做任何限制、过滤、或者假设 ——数据在写入时是什么样的,它被读取时就是什么样。
这也是我们将 SDS 的 buf
属性称为字节数组的原因 ——Redis 不是用这个数组来保存字符,而是用它来保存一系列二进制数据。
比如说,使用 SDS 来保存之前提到的特殊数据格式就没有任何问题,因为 SDS 使用 len
属性的值而不是空字符来判断字符串是否结束,如图 2-18 所示。
buf"]; buf [label = " { 'R' | 'e' | 'd' | 'i' | 's' | '\\0' | 'C' | 'l' | 'u' | 's' | 't' | 'e' | 'r' | '\\0' | '\\0' } "]; // sdshdr:buf -> buf;}">
通过使用二进制安全的 SDS ,而不是 C 字符串,使得 Redis 不仅可以保存文本数据,还可以保存任意格式的二进制数据。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









2 進数演算は 2 進数に基づいた演算方法であり、その基本演算には加算、減算、乗算、除算が含まれます。 2 進算術には、基本的な演算に加えて、論理演算、変位演算、その他の演算も含まれます。論理演算には AND、OR、NOT などの演算が含まれ、変位演算には左シフト演算と右シフト演算が含まれます。これらの操作には、対応するルールとオペランド要件があります。

EDVAC には 2 つの大きな改良点があります。1 つはバイナリの使用、もう 1 つは保存されたプログラムの完成です。これは、あるプログラム命令から次のプログラム命令に自動的に進み、その操作は命令を通じて自動的に完了できます。 「命令」にはデータやプログラムが含まれており、それらはコードの形で機械の記憶装置に入力されます、つまり、データを格納する同じ記憶装置を使用して、演算を実行するための命令を格納するという新しい概念です。 - 保存されたプログラムと呼ばれます。

2 進数は 1 と 0 で表されます。 16 ビットの 16 進数体系は、2 進表現から 16 進表現に変換するために、{0,1,2,3…..9,A(10),B(11),…F(15)} となります。文字列 ID は、最下位側から始まるニブルと呼ばれる 4 ビットのチャンクにグループ化されます。各ブロックは、対応する 16 進数に置き換えられます。 16 進数と 2 進数の表現を明確に理解するために例を見てみましょう。 001111100101101100011101 3 E 5 B&N

Golang でバイナリ ファイルを読み取るにはどうすればよいですか?バイナリ ファイルは、コンピュータが認識して処理できるデータを含む、バイナリ形式で保存されたファイルです。 Golang では、いくつかのメソッドを使用してバイナリ ファイルを読み取り、必要なデータ形式に解析できます。 Golangでバイナリファイルを読み込む方法と具体的なコード例を紹介します。まず、OS パッケージの Open 関数を使用してバイナリ ファイルを開く必要があります。これにより、ファイル オブジェクトが返されます。それから私たちは作ることができます

コンピュータが 2 進法を使用する主な理由: 1. コンピュータは論理回路で構成されています。論理回路には通常、スイッチのオンとオフの 2 つの状態しかなく、これら 2 つの状態は「1」と「0」で表すことができます。 . 2 進法では 0 と 1 の 2 つの数値のみが使用されるため、送信時や処理時にエラーが発生しにくく、コンピュータの信頼性が高くなります。

タイトル: Go 言語で 16 進数を 2 進数に変換する方法を簡単に学習します。特定のコード例が必要です。コンピューター プログラミングでは、異なる基数間の変換操作がよく使用されます。その中でも、16 進数と 2 進数間の変換は比較的一般的です。 Go 言語では、いくつかの簡単なコード例を通じて 16 進数から 2 進数への変換を実現できます。一緒に学びましょう。まずは16進数と2進数の表現方法を見てみましょう。 16 進数は数値を表す方法で、0 ~ 9 と A ~ F を使用して 1 を表します。

コンピューターでは負の数は 2 の補数を使用して表現されます。つまり、負の数は正の数の 2 の補数によって表現されます。

Golang はバイナリ ファイルを扱えますか? Go 言語では、バイナリ ファイルの処理は非常に一般的で便利です。組み込みのパッケージとメソッドを使用することで、バイナリ ファイルの読み取り、書き込み、操作を簡単に行うことができます。この記事では、Go でバイナリ ファイルを処理する方法を説明し、具体的なコード例を示します。バイナリ ファイルの読み取り バイナリ ファイルを読み取るには、まずファイルを開いて、対応するファイル オブジェクトを作成する必要があります。次に、Read メソッドを使用してファイルからデータを読み取り、それをバイト単位で保存します。
