Deno bytes 模塊全解析

2021-03-02 全棧修仙之路

本文在介紹 ArrayBuffer 和 TypedArray 的基礎上,詳細剖析了 Deno bytes 模塊的功能與具體實現,並站在 v8 的角度簡單分析了 JSArrayBuffer 和 JSTypedArray 類。

一、基礎知識
1.1 ArrayBuffer

ArrayBuffer 對象用來表示通用的、固定長度的原始二進位數據緩衝區。ArrayBuffer 不能直接操作,而是要通過類型數組對象 或 DataView 對象來操作,它們會將緩衝區中的數據表示為特定的格式,並通過這些格式來讀寫緩衝區的內容。

ArrayBuffer 簡單說是一片內存,但是你不能直接用它。這就好比你在 C 裡面,malloc 一片內存出來,你也會把它轉換成 unsigned_int32 或者 int16 這些你需要的實際類型的數組/指針來用。

這就是 JS 裡的 TypedArray 的作用,那些 Uint32Array 也好,Int16Array 也好,都是給 ArrayBuffer 提供了一個 「View」,MDN 上的原話叫做 「Multiple views on the same data」,對它們進行下標讀寫,最終都會反應到它所建立在的 ArrayBuffer 之上。

來源:https://www.zhihu.com/question/30401979

語法

new ArrayBuffer(length)

參數:length 表示要創建的 ArrayBuffer 的大小,單位為字節。返回值:一個指定大小的 ArrayBuffer 對象,其內容被初始化為 0。異常:如果 length 大於 Number.MAX_SAFE_INTEGER(>= 2 ** 53)或為負數,則拋出一個  RangeError  異常。示例

下面的例子創建了一個 8 字節的緩衝區,並使用一個 Int32Array 來引用它:

let buffer = new ArrayBuffer(8);
let view   = new Int32Array(buffer);

從 ECMAScript 2015 開始,ArrayBuffer 對象需要用 new 運算符創建。如果調用構造函數時沒有使用 new,將會拋出 TypeError  異常。比如執行該語句 let ab = ArrayBuffer(10) 將會拋出以下異常:

VM109:1 Uncaught TypeError: Constructor ArrayBuffer requires 'new'
at ArrayBuffer (<anonymous>)
at <anonymous>:1:10

對於一些常用的 Web API,如 FileReader API 和 Fetch API 底層也是支持 ArrayBuffer,這裡我們以  FileReader API 為例,看一下如何把 File 對象讀取為 ArrayBuffer 對象:

const reader = new FileReader();

reader.onload = function(e) {
  let arrayBuffer = reader.result;
}

reader.readAsArrayBuffer(file);

1.2 Unit8Array

Uint8Array 數組類型表示一個 8 位無符號整型數組,創建時內容被初始化為 0。創建完後,可以以對象的方式或使用數組下標索引的方式引用數組中的元素。

語法

new Uint8Array(); // ES2017 最新語法
new Uint8Array(length); // 創建初始化為0的,包含length個元素的無符號整型數組
new Uint8Array(typedArray);
new Uint8Array(object);
new Uint8Array(buffer [, byteOffset [, length]]);

示例
// new Uint8Array(length); 
var uint8 = new Uint8Array(2);
uint8[0] = 42;
console.log(uint8[0]); // 42
console.log(uint8.length); // 2
console.log(uint8.BYTES_PER_ELEMENT); // 1

// new TypedArray(object); 
var arr = new Uint8Array([21,31]);
console.log(arr[1]); // 31

// new Uint8Array(typedArray);
var x = new Uint8Array([21, 31]);
var y = new Uint8Array(x);
console.log(y[0]); // 21

// new Uint8Array(buffer [, byteOffset [, length]]);
var buffer = new ArrayBuffer(8);
var z = new Uint8Array(buffer, 1, 4);

// new TypedArray(object); 
// 當傳入一個 object 作為參數時,就像通過 TypedArray.from() 
// 方法創建一個新的類型化數組一樣。
var iterable = function*(){ yield* [1,2,3]; }(); 
var uint8 = new Uint8Array(iterable); 
// Uint8Array[1, 2, 3]

1.3 ArrayBuffer 和 TypedArray

ArrayBuffer 本身只是一行 0 和 1 串。ArrayBuffer 不知道該數組中第一個元素和第二個元素之間的分隔位置。

(圖片來源 —— A cartoon intro to ArrayBuffers and SharedArrayBuffers)

為了提供上下文,實際上要將其分解為多個盒子,我們需要將其包裝在所謂的視圖中。可以使用類型數組添加這些數據視圖,並且你可以使用許多不同類型的類型數組。

例如,你可以有一個 Int8 類型的數組,它將把這個數組分成 8-bit 的字節數組。

(圖片來源 —— A cartoon intro to ArrayBuffers and SharedArrayBuffers)

或者你也可以有一個無符號 Int16 數組,它會把數組分成 16-bit 的字節數組,並且把它當作無符號整數來處理。

(圖片來源 —— A cartoon intro to ArrayBuffers and SharedArrayBuffers)

你甚至可以在同一基本緩衝區上擁有多個視圖。對於相同的操作,不同的視圖會給出不同的結果。

例如,如果我們從這個 ArrayBuffer 的 Int8 視圖中獲取 0 & 1 元素的值(-19 & 100),它將給我們與 Uint16 視圖中元素 0 (25837)不同的值,即使它們包含完全相同的位。

(圖片來源 —— A cartoon intro to ArrayBuffers and SharedArrayBuffers)

這樣,ArrayBuffer 基本上就像原始內存一樣。它模擬了使用 C 之類的語言進行的直接內存訪問。你可能想知道為什麼我們不讓程序直接訪問內存,而是添加了這種抽象層,因為直接訪問內存將導致一些安全漏洞

1.4 v8 句柄

句柄提供對 JavaScript 對象在堆中位置的引用。V8 垃圾收集器回收了無法再訪問的對象所使用的內存。在垃圾收集過程中,垃圾收集器通常將對象移動到堆中的不同位置。當垃圾收集器移動對象時,垃圾收集器還會使用對象的新位置來更新所有引用該對象的句柄。

如果無法從 JavaScript 訪問對象並且沒有引用該對象的句柄,則該對象被視為垃圾。垃圾收集器會不時刪除所有被視為垃圾的對象。V8 的垃圾回收機制是 V8 性能的關鍵。

句柄在 V8 中只是一個統稱,它其實還分為多種類型:

本地句柄(v8::Local):本地句柄保存在堆棧中,並在調用適當的析構函數時被刪除。這些句柄的生存期由一個句柄作用域決定,該作用域通常是在函數調用開始時創建的。刪除句柄作用域後,垃圾回收器可以自由地釋放先前由句柄作用域中的句柄引用的那些對象,前提是它們不再可從 JavaScript 或其他句柄訪問。持久句柄(v8::Persistent):持久句柄提供對堆分配的 JavaScript 對象的引用,就像本地句柄一樣。有兩種類型,它們處理的引用的生存期管理不同。當需要為多個函數調用保留對一個對象的引用時,或者當句柄生存期不對應於 C++ 作用域時,請使用持久句柄。永生句柄(v8::Eternal):Eternal 是用於永遠不會被刪除的 JavaScript 對象的持久句柄。它的使用成本更低,因為它使垃圾回收器不必確定對象的活動性。

用一個更形象的比喻,那麼 v8::Local 更像是 JavaScript 中的 let 。在 V8 中,內存的分配都交付給了 V8,那麼我們就最好不要使用自己的 new 方法來創建對象,而是使用 v8::Local 裡的各種方法來創建一個對象。由 v8::Local 創建的對象,能夠被 v8 自動進行管理,也就是傳說中的GC (垃圾清理機制)。

Persistent 代表的是持久的意思,更類似全局變量,申請和釋放一定要記得使用:Persistent::New,Persistent::Dispose 這兩個方法,否則會內存側漏。

來源於:https://zhuanlan.zhihu.com/p/35371048 —— V8概念以及編程入門

二、Bytes 模塊詳解

bytes 模塊旨在為字節切片的操作提供支持,接下來我們將逐一分析該模塊提供的所有方法。

2.1 repeat

作用:重複給定二進位數組的字節並返回新的二進位數組。

使用示例:

import { repeat } from "https://deno.land/std/bytes/mod.ts";

repeat(new Uint8Array([1]), 3); // returns Uint8Array(3) [ 1, 1, 1 ]

源碼實現:

// std/bytes/mod.ts
import { copyBytes } from "../io/util.ts";

export function repeat(b: Uint8Array, count: number): Uint8Array {
  if (count === 0) {
    return new Uint8Array();
  }

  if (count < 0) {
    throw new Error("bytes: negative repeat count");
  } else if ((b.length * count) / count !== b.length) {
    throw new Error("bytes: repeat count causes overflow");
  }

  const int = Math.floor(count);

  if (int !== count) {
    throw new Error("bytes: repeat count must be an integer");
  }

  // 根據源Uint8Array的長度與重複次數來創建新的空間 
  const nb = new Uint8Array(b.length * count);

  // 執行字節拷貝任務
  let bp = copyBytes(b, nb);

  for (; bp < nb.length; bp *= 2) {
    copyBytes(nb.slice(0, bp), nb, bp);
  }

  return nb;
}

在以上代碼中,會對 count 參數的值進行各種校驗,從而保證代碼的安全性。之後,會根據源Uint8Array的長度與重複次數來創建新的空間,然後使用封裝 copyBytes 方法執行字節拷貝操作。這裡我們從 V8 的角度來簡單認識一下 ArrayBuffer 和 Uint8Array 。

在 src/api/api.h 文件中,我們可以看到 DECLARE_OPEN_HANDLE 和 OPEN_HANDLE_LIST 這兩個宏:

// src/api/api.h 
#define DECLARE_OPEN_HANDLE(From, To)                              \
  static inline v8::internal::Handle<v8::internal::To> OpenHandle( \
      const From* that, bool allow_empty_handle = false);

  OPEN_HANDLE_LIST(DECLARE_OPEN_HANDLE)
    
#define OPEN_HANDLE_LIST(V)                    \
  V(Template, TemplateInfo)                    \
  V(ArrayBuffer, JSArrayBuffer)                \
  V(ArrayBufferView, JSArrayBufferView)        \
  V(TypedArray, JSTypedArray)                  \
  V(Uint8Array, JSTypedArray)                  \
  V(Uint8ClampedArray, JSTypedArray)           \
  V(Int8Array, JSTypedArray)                   \
  V(Uint16Array, JSTypedArray)                 \
  V(Int16Array, JSTypedArray)                  \
  V(Uint32Array, JSTypedArray)                 \
  V(Int32Array, JSTypedArray)                  \
  V(Float32Array, JSTypedArray)                \
  V(Float64Array, JSTypedArray)                \
  V(DataView, JSDataView)                      \
  V(SharedArrayBuffer, JSArrayBuffer)          \
  ...

接著我們來看一下 ArrayBuffer 和 Uint8Array 經過宏替換後的結果:

static inline v8::internal::Handle<v8::internal::JSArrayBuffer> OpenHandle( \
      const ArrayBuffer* that, bool allow_empty_handle = false);

static inline v8::internal::Handle<v8::internal::JSTypedArray> OpenHandle( \
      const Uint8Array* that, bool allow_empty_handle = false);

下面我們順藤摸瓜,先找到 JSArrayBuffer 類:

// src/objects/js-array-buffer.h
namespace v8 {
namespace internal {

class ArrayBufferExtension;

class JSArrayBuffer
    : public TorqueGeneratedJSArrayBuffer<JSArrayBuffer, JSObject> {
 public:
// V8支持的的JSArrayBuffer的最大長度
// 在32位架構上,我們將此限制為2GB。因此,我們可以繼續使用
// Unsigned31 校驗邊界來限制其最大長度。      
#if V8_HOST_ARCH_32_BIT
  static constexpr size_t kMaxByteLength = kMaxInt;
#else
// 對於非32位架構,如64位架構,最大值為2^53-1    
  static constexpr size_t kMaxByteLength = kMaxSafeInteger;
#endif
  }
}

上述代碼中 kMaxSafeInteger 的定義如下:

// ES6 section 20.1.2.6 Number.MAX_SAFE_INTEGER
constexpr uint64_t kMaxSafeIntegerUint64 = 9007199254740991;  // 2^53-1
constexpr double kMaxSafeInteger = static_cast<double>(kMaxSafeIntegerUint64);

這裡知道對於非 32 位架構,JSArrayBuffer 的大小最大為 2^53-1。那為什麼是這個值呢?這裡我們得先來了解一下 Number.MAX_SAFE_INTEGER 常量,它表示在 JavaScript 中最大的安全整數(2^53-1)。

MAX_SAFE_INTEGER 是一個值為 9007199254740991 的常量。因為 JavaScript 的數字存儲使用了 IEEE 754 中規定的雙精度浮點數數據類型,而這一數據類型能夠安全存儲 -(2^53 - 1) 到 2^53 - 1 之間的數值(包含邊界值)。

這裡安全存儲的意思是指能夠準確區分兩個不相同的值,例如 Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 將得到 true 的結果,而這在數學上是錯誤的,參考 Number.isSafeInteger() 獲取更多信息。

前面我們已經提到了創建 ArrayBuffer 的語法是:new ArrayBuffer(length),其中參數 length 的類型是 Number 類型,所以其對應的最大的安全整數為(2^53-1)。

介紹完上述內容我們再來看一下 repeat 函數中的 (b.length * count) / count !== b.length 這行代碼:

// std/bytes/mod.ts
export function repeat(b: Uint8Array, count: number): Uint8Array {
  if (count < 0) {
      throw new Error("bytes: negative repeat count");
  } else if ((b.length * count) / count !== b.length) {
      throw new Error("bytes: repeat count causes overflow");
  }
  //...
}

為什麼通過 (b.length * count) / count !== b.length 這行代碼可以判斷是否越界呢?這裡廢話不多說,我們直接看以下計算結果:

(9007199254740991 * 1.1) / 1.1
9007199254740990

好的,下面我們繼續介紹如何創建 Handle<JSTypedArray> 句柄:

// src/heap/factory.cc
Handle<JSTypedArray> Factory::NewJSTypedArray(ExternalArrayType type,
                                              Handle<JSArrayBuffer> buffer,
                                              size_t byte_offset,
                                              size_t length) {
  size_t element_size;
  ElementsKind elements_kind;
  ForFixedTypedArray(type, &element_size, &elements_kind);
  size_t byte_length = length * element_size;

  CHECK_LE(length, JSTypedArray::kMaxLength);
  CHECK_EQ(length, byte_length / element_size);
  CHECK_EQ(0, byte_offset % ElementsKindToByteSize(elements_kind));

  Handle<Map> map;
  switch (elements_kind) {
#define TYPED_ARRAY_FUN(Type, type, TYPE, ctype)                              \
  case TYPE##_ELEMENTS:                                                       \
    map =                                                                     \
        handle(isolate()->native_context()->type##_array_fun().initial_map(), \
               isolate());                                                    \
    break;

    TYPED_ARRAYS(TYPED_ARRAY_FUN)
#undef TYPED_ARRAY_FUN

    default:
      UNREACHABLE();
  }
  Handle<JSTypedArray> typed_array =
      Handle<JSTypedArray>::cast(NewJSArrayBufferView(
          map, empty_byte_array(), buffer, byte_offset, byte_length));
  typed_array->set_length(length);
  typed_array->SetOffHeapDataPtr(isolate(), buffer->backing_store(),
                                 byte_offset);
  return typed_array;
}

通過觀察上述代碼,我們可以知道再創建 Handle<JSTypedArray> 句柄時,會先使用 NewJSArrayBufferView 對 JSArrayBuffer 對象進行封裝,然後再調用 Handle<JSTypedArray>::cast 方法把 NewJSArrayBufferView 對象轉換為最終的 Handle<JSTypedArray> 。

這裡也進一步印證前面提到的內容:即 ArrayBuffer 不能直接操作,而是要通過 TypedArray 對象或 DataView 對象來操作,它們會將緩衝區中的數據表示為特定的格式,並通過這些格式來讀寫緩衝區的內容。

2.2 concat

作用:合併兩個二進位數組並返回新的二進位數組。

使用示例:

import { concat } from "https://deno.land/std/bytes/mod.ts";

concat(new Uint8Array([1, 2]), new Uint8Array([3, 4]));
// returns Uint8Array(4) [ 1, 2, 3, 4 ]

源碼實現:

export function concat(a: Uint8Array, b: Uint8Array): Uint8Array {
  const output = new Uint8Array(a.length + b.length);
  output.set(a, 0);
  output.set(b, a.length);
  return output;
}

在 concat 方法體中,Uint8Array 對象的 set 方法用於從指定數組中讀取值,並將其存儲在類型化數組中。該方法的籤名是:

typedarray.set(array[, offset])
typedarray.set(typedarray[, offset])

其中 offset 參數是可選的,該參數指定從什麼地方開始使用源數組的值進行寫入操作。如果忽略該參數,則默認為 0(也就是說,從目標數組的下標為 0 處開始,使用源數組的值覆蓋重寫)。

2.3 findIndex

作用:從給定的二進位數組中查找二進位模式的第一個索引。

使用示例:

import { findIndex } from "https://deno.land/std/bytes/mod.ts";

findIndex(
  new Uint8Array([1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 3]),
  new Uint8Array([0, 1, 2])
);
// => returns 2

源碼實現:

// std/bytes/mod.ts
export function findIndex(a: Uint8Array, pat: Uint8Array): number {
  const s = pat[0];
  for (let i = 0; i < a.length; i++) {
    if (a[i] !== s) continue;
    // 記錄第一個匹配元素的位置
    const pin = i;
    // 已匹配的元素個數
    let matched = 1;
    let j = i;
    // 循環匹配其餘的元素
    while (matched < pat.length) {
      j++;
      if (a[j] !== pat[j - pin]) {
        break;
      }
      matched++;
    }
    if (matched === pat.length) {
      return pin;
    }
  }
  return -1;
}

2.4 findLastIndex

作用:從給定的二進位數組中查找二進位模式的最後一個索引。

使用示例:

import { findLastIndex } from "https://deno.land/std/bytes/mod.ts";

findLastIndex(
  new Uint8Array([0, 1, 2, 0, 1, 2, 0, 1, 3]),
  new Uint8Array([0, 1, 2])
);
// => returns 3

源碼實現:

// std/bytes/mod.ts
export function findLastIndex(a: Uint8Array, pat: Uint8Array): number {
  const e = pat[pat.length - 1];
  for (let i = a.length - 1; i >= 0; i--) {
    if (a[i] !== e) continue;
    const pin = i;
    let matched = 1;
    let j = i;
    while (matched < pat.length) {
      j--;
      if (a[j] !== pat[pat.length - 1 - (pin - j)]) {
        break;
      }
      matched++;
    }
    if (matched === pat.length) {
      return pin - pat.length + 1;
    }
  }
  return -1;
}

2.5 equal

作用:檢查給定的二進位數組是否相等。

使用示例:

import { equal } from "https://deno.land/std/bytes/mod.ts";

equal(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2, 3])); // returns true
equal(new Uint8Array([0, 1, 2, 3]), new Uint8Array([0, 1, 2, 4])); // returns false

源碼實現:

// std/bytes/mod.ts
export function equal(a: Uint8Array, match: Uint8Array): boolean {
  // 優先判斷TypedArray數組的長度是否相等
  if (a.length !== match.length) return false;
  // 對TypedArray數組的每一項進行比對
  for (let i = 0; i < match.length; i++) {
    if (a[i] !== match[i]) return false;
  }
  return true;
}

2.6 hasPrefix

作用:檢查二進位數組是否具有二進位前綴。

使用示例:

import { hasPrefix } from "https://deno.land/std/bytes/mod.ts";

hasPrefix(new Uint8Array([0, 1, 2]), new Uint8Array([0, 1])); // returns true
hasPrefix(new Uint8Array([0, 1, 2]), new Uint8Array([1, 2])); // returns false

源碼實現:

// std/bytes/mod.ts
export function hasPrefix(a: Uint8Array, prefix: Uint8Array): boolean {
  for (let i = 0, max = prefix.length; i < max; i++) {
    if (a[i] !== prefix[i]) return false;
  }
  return true;
}

三、參考資源a-cartoon-intro-to-arraybuffers-and-sharedarraybuffers

相關焦點

  • 2021再看Deno
    default async function (ctx) { return fetch('/api/isLogin') .then(res => res.json()) .then(res => { const {success, data: {isLogin} = {}} = res;
  • Deno並不是下一代Node.js
    這幾天前端圈最火的事件莫過於 ry(Ryan Dahl) 的新項目 deno 了,很多 IT 新聞和媒體都用了標題:「下一代 Node.js」。這周末讀了一遍 deno 的源碼,特意寫了這篇文章。長文預警(5000字,11圖)。0. 為什麼開發 Deno?
  • Node.js VS Deno:你應該選擇後者的6個原因
    denoscoop install denocargo install deno讓我們弄清楚我們的Deno之旅從一個TypeScript文件開始。標準庫的好處在於,你可以在所有的Deno項目中使用它,而不必依賴於從npm中導入模塊。5.沒有海量的Node Modules文件夾相反,我們使用現代的ES模塊語法導入軟體包。在這裡,遠程模塊由其URL引用。當你第一次運行腳本時,它將在本地下載此代碼並進行緩存。
  • 2021再看Deno(關於CDN for JavaScript modules的思考)
    2018年,我曾經在deno發布不久寫過一篇《Deno不是下一代Node.js!》的文章,正好最近有一些研究,站在2021年再來看看deno。
  • 又一陣後浪:橫空出世的Deno會取代NodeJS嗎?
    如果你需要讓你的腳本訪問/etc 文件夾,可以輸入:deno --allow-read=/etcmyscript.ts你的代碼將從文件夾中讀取,你會得到一個安全異常提示。這類似於其他平臺處理安全性的方式。
  • 沒有等來Node.js 2.0 Node 之父卻帶來新作Deno 1.0
    在這個版本中,它的做法是:允許你從任何地方導入你所需要的模塊。換句話說,你可以簡單地執行以下操作:import * as log from "https://deno.land/std/log/mod.ts";Deno 不再需要擁有自己的集中式存儲庫,但是在執行此操作時你必須謹慎,因為從無法控制的第三方來源導入模塊,可能會導致你處於公開狀態,並暴露於外部世界中。
  • JavaScript 開發人員更喜歡 Deno 的五大原因
    現代化的 Javascript——ES 模塊如果你也像我一樣是一位 React 開發人員,你會注意到使用 NodeJS 導入軟體包的語法是不一樣的。這是因為 Node 面世於 2009 年,這些年來 Javascript 得到了大量更新和改進。
  • Deno 1.5.4 發布,基於 V8 的 TypeScript 運行時
    此版本更新內容如下: feat(unstable):添加 deno test --no-run feat(unstable):支持 --watch flag 的 bundle 和 fmt 子命令 修復:支持"deno run --v8-flags=--help" without script fix(tsc)
  • deno 將於今日發布 v1.0,一個替代 Node.js 的不二選擇
    deno 是一個 JavaScript / TypeScript 運行時,具有安全的默認設置和良好的開發體驗,構建在 V8 引擎、Rust 語言和 Tokio 之上。最初的 deno 版本是用 Go 語言寫就,但在第二版中移除了 Go 轉而用 Rust 語言替代,因為後者具有更好的安全性。
  • Python模塊學習 ---- zipfile模塊
    compression=zipfile.ZIP_DEFLATED) #py<=3.6#fzip=zipfile.ZipFile(orgfilename+'.zip','w',compression=zipfile.ZIP_DEFLATED,compresslevel=6) #py>=3.7fzip.comment=comment.encode('utf-8') # bytes
  • Python bytes類型及用法
    說白了,bytes 只是簡單地記錄內存中的原始數據,至於如何使用這些數據,bytes 並不在意,你想怎麼使用就怎麼使用,bytes 並不約束你的行為。bytes 類型的數據非常適合在網際網路上傳輸,可以用於網絡通信編程;bytes 也可以用來存儲圖片、音頻、視頻等二進位格式的文件。
  • 構建 Python C 擴展模塊
    char *str, *filename = NULL;    int bytes_copied = -1;    /* Parse arguments */    if(!return PyLong_FromLong(bytes_copied);}接下來,我們來一點點分析上面的代碼。
  • Deno 繼顛覆 Node 之後,又「內部」拒絕了 TypeScript
    Deno 團隊提出的一大現實問題,是 TypeScript 會在兩個位置複製相互獨立的 Body 類,https://github.com/denoland/deno/issues/4748 由於 TypeScript 編譯器無法幫助開發者生成 d.ts 文件,內部代碼與運行時 TypeScript 聲明必須以手動方式保持同步;
  • bytes對象,不可變的字節序列
    為此, Python 額外提供了字節序列對象 —— bytes 。好了,我們已經弄明白 str 對象以 bytes 之間的關係,這兩者是 Python 中最重要的內建對象之一。讀者對 str 對象應該再熟悉不過了,但對更接近底層的 bytes 對象可能涉獵不多。沒關係,經過本節學習,你將徹底掌握它!
  • Deno VS Node:Javascript開發人員更喜歡前者的五大原因
    1.Modern Javascript —ES 模塊如果你也是個React開發人員,那麼應該已經注意到了,在使用NodeJS時導入包的語法是不同的。這是因為Node是在2009年開發的,從那時起,針對Javascript進行了很多更新和改進。
  • python基礎-bytes和bytearray的用法
    bytes二進位序列類型指定長度的零填充字節對象: bytes(3)二進位字符串對象:bytes(b'abc')bytearray二進位數組指定長度的零填充字節對象: bytearray(3)二進位字符串對象:bytearray(b'abc')實例:print(type("ffff
  • 計算機英語:位與字節(Bits & Bytes)
    KB = kilobyte = about 1,000 (one thousand) bytes, (1024 or 2^10)   MB = megabyte = about 1,000,000 (one million) bytes, (1,048,576 or 2^20)   GB= gigabyte = about
  • python3 還原bytes為中文的折騰
    linux默認使用utf-8編碼,而utf-8默認以unicode字符集進行的編碼,此種格式其實是一種utf-8的字節碼[bytes]。如果要還原,需要對其進行utf-8解碼這次知道是啥了,那就知道要怎麼處理了,python中有很多方法可以編碼、解碼,於是開始第二個過程二、怎麼還原成中文?
  • Node.js 高級進階之 fs 文件模塊學習
    今日文章由 「程式設計師成長指北@koala」 授權分享,正文從下面開始~前言文件操作是開發過程中並不可少的一部分,作為一名 Node.js
  • Python 3中str與bytes的區分
    文本總是Unicode,由str類型表示,二進位數據則由bytes類型表示。Python 3不會以任意隱式的方式混用str和bytes,正是這使得兩者的區分特別清晰。你不能拼接字符串和字節包,也無法在字節包裡搜索字符串(反之亦然),也不能將字符串傳入參數為字節包的函數(反之亦然)。這是件好事。