Chính xác thì loại char char một byte hoạt động như thế nào trong PostgreSQL?


9

Tôi thường thấy mọi người nói về "char". Tôi chưa bao giờ sử dụng nó. Nó được định nghĩa trong các tài liệu là,

Loại "char" (lưu ý các trích dẫn) khác với char (1) ở chỗ nó chỉ sử dụng một byte lưu trữ. Nó được sử dụng nội bộ trong các danh mục hệ thống như là một kiểu liệt kê đơn giản.

Và xa hơn,

"char"  1 byte  single-byte internal type

Vì vậy, nếu đó là một byte, tên miền là gì và bạn sẽ sử dụng nó như thế nào? Được ký hay chưa ký? Trong bài đăng này của @Erwin Brandstetter, anh ấy đã đưa nó ra , nhưng tôi vẫn bối rối. Ông đang sử dụng ascii()chr()và cung cấp này

SELECT i
     , chr(i)::"char"        AS i_encoded
     , ascii(chr(i)::"char") AS i_decoded
FROM   generate_series(1,256) i;

Đó là làm một cái gì đó thực sự kỳ lạ giữa 10 và 11.

  i  | i_encoded | i_decoded 
-----+-----------+-----------
...
   8 | \x08      |         8
   9 |           |         9
  10 |          +|        10
     |           |           -- WTF is going on here.
  11 | \x0B      |        11
  12 | \x0C      |        12
...

Nó cũng trở nên thực sự kỳ lạ ở đây:

 126 | ~         |       126
 127 | \x7F      |       127
 128 |           |       128
 129 |           |       128
 130 |           |       128
 131 |           |       128

Tại sao mọi thứ ở phía bắc của 128 được giải mã là 128? Nhưng để tăng tốc độ lên một chút, sau 192 có một công tắc và chúng được giải mã thành 192 ..

 190 |           |       128
 191 |           |       128
 192 |           |       192
 193 |           |       192
 194 |           |       192
 195 |           |       192
 196 |           |       192
 197 |           |       192

Erwin nói

Có một số ký tự không có nghĩa là để hiển thị. Vì vậy, mã hóa trước khi bạn lưu trữ và giải mã trước khi bạn hiển thị ...

Tôi không chắc tại sao chúng ta nên mã hóa tất cả mặc dù nếu chúng ta đang làm chính xác những gì câu hỏi đó yêu cầu

CREATE TABLE foo AS
SELECT i::"char"
FROM   generate_series(-128,127) i;

Điều đó làm việc tốt. Chúng ta có thể lấy lại ints bằng cách sử dụng

SELECT i::int FROM foo;

Vì vậy, trong ngắn hạn,

  1. Mã của Erwin đang làm gì trong khoảng thời gian từ 10-11 đến khi tôi không có giá trị?
  2. Tại sao 128 lặp đi lặp lại nhiều lần như vậy?
  3. Tại sao 192 lặp đi lặp lại nhiều lần như vậy?
  4. Làm cách nào để tôi kích hoạt không thể lưu trữ 0, khi Erwin nói rằng bạn không thể mã hóa 0 theo cách này (ký tự null không được phép)

    CREATE TABLE foo AS SELECT 0::int::"char" AS x;
    SELECT x::int FROM foo;
     x 
    ---
    0
    

Câu trả lời:


11

1. chr(10)

... Tạo ra ký tự LINEFEED (còn gọi là chuỗi thoát \n) và psql hiển thị ký tự đó bằng một dòng mới (được chỉ định bởi +). Mọi thứ đều đúng ở đó.

2. & 3. ascii()tạo ra 128 hay 192?

Nó bắt đầu với một sai lầm tôi đã làm. Tôi vô tình thừa nhận "char"sẽ bao gồm phạm vi của một unsigned 1 byte số nguyên (0 đến 255) trong câu trả lời tham chiếu (nay là cố định), nhưng nó thực sự là phạm vi của một số nguyên 1 byte (-128 đến 127) trong nội bộ.

ascii()nhận một texttham số, truyền từ ẩn "char"để texttạo ra một ký tự được mã hóa đa chuỗi trong unicode và hàm trả về ( theo tài liệu trênascii() ):

Mã ASCII của ký tự đầu tiên của đối số. Đối với UTF8 trả về điểm mã Unicode của ký tự. Đối với các mã hóa đa bào khác, đối số phải là ký tự ASCII.

Vì vậy, chúng tôi nhận được rất nhiều giá trị cắt ngắn. 128 và 192 là các giá trị byte cho byte hàng đầu của các ký tự đa dòng.

4. byte rỗng

Không có khả năng lưu trữ vô byte chỉ ảnh hưởng đến các loại nhân vật thường xuyên ( text, char, varchar), không "char". Nó áp dụng cho ví dụ lỗi của tôi, bởi vì tôi chọn textlàm bước đệm. Trong khi đúc giữa "char"integertrực tiếp, giới hạn không áp dụng. Hướng dẫn về chr():

Ký tự NULL (0) không được phép vì các kiểu dữ liệu văn bản không thể lưu trữ các byte như vậy.

Không phải như vậy đối với "char", nơi 0được ánh xạ tới chuỗi trống '':

SELECT ''::"char"::int  -- 0
     , 0::"char" = '';  -- t

Hãy nhớ rằng: "char"vẫn là một loại "nội bộ" dành cho liệt kê đơn giản và rẻ tiền. Không được thiết kế chính thức cho những gì chúng tôi đang làm ở đây và không thể chuyển sang RDBMS khác. Không có đảm bảo của dự án Postgres cho việc này.


Tôi vẫn nghĩ rằng kết quả của màn hình \ r psqllà một lỗi hoặc một cái gì đó kỳ lạ. Nó kết thúc dòng, và sau đó bỏ qua một dòng?
Evan Carroll

4
@Evan không, nó không 'bỏ qua một dòng', dòng trống là phần tiếp theo của hàng trước đó (đó là multiline). Nếu bạn có thể yêu cầu psql vẽ các đường ngang giữa các hàng đầu ra thì điều này sẽ rõ ràng hơn, nhưng vì bạn không thể là đầu mối trực quan là '+'.
Jack nói hãy thử topanswers.xyz

0

Để thực hiện chuyển sang phạm vi đã ký, bạn có thể tạo một số chức năng để trợ giúp. Danh sách này sẽ tạo ra các chức năng không phôi để giúp đỡ hỗ trợ trong quá trình này đi từ một one-byte phạm vi int unsigned của[0-255] một phạm vi một byte ký mà nhân vật đòi hỏi của[-128,127] .

Thí dụ

Một trích xuất từ ​​README

Bây giờ bạn có thể làm ví dụ lưu trữ các giá trị trong phạm vi [0-255]trên bảng.

CREATE TABLE t(x) AS VALUES
  (to_uchar(255)),
  (to_uchar(0));

Chuyển đổi chúng thành bit(8)

SELECT to_bit8(x) FROM t;
 to_bit8  
----------
 11111111
 00000000
(2 rows)

Có lẽ bạn muốn xóa hai bit thứ tự thấp hơn, bạn có thể làm điều đó với BITWISE-AND,

UPDATE t
  SET x = to_uchar( to_bit8(x) & (x'fc')::bit(8) );

SELECT to_bit8(x) FROM t;
 to_bit8  
----------
 11111100
 00000000
(2 rows)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.