一字节的“ char”类型在PostgreSQL中如何工作?


9

我经常看到人们在谈论"char"。我没用过 在文档中定义为

类型“ char”(请注意引号)与char(1)的不同之处在于,它仅使用一个字节的存储空间。在系统目录中内部使用它作为一种简单的枚举类型。

并进一步,

"char"  1 byte  single-byte internal type

那么,如果它是一个字节,则域是什么,您将如何使用它?它是签名的还是未签名的?在@Erwin Brandstetter的这篇文章中,他对此进行了阐述,但我仍然感到困惑。他正在使用ascii()chr(),并提供了

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

这在10到11之间确实很奇怪。

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

这里也真的很奇怪:

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

为什么将128以北的所有内容都解码为128?但是,为了使座谈会稍微增加一点,在192之后有一个开关,它们被解码为192。

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

欧文说

有几个字符不适合显示。因此,在存储之前先编码,在显示之前先解码...

我不确定为什么我们要完全编码,即使我们正在做的是问题所要求的

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

很好 我们可以使用

SELECT i::int FROM foo;

简而言之,

  1. 在i为空的10-11之间,欧文的代码在做什么?
  2. 为什么重复128次如此多次?
  3. 为什么192次重复这么多次?
  4. 当Erwin说无法以这种方式编码0(不允许空字符)时,如何触发无法存储0

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

Answers:


11

1。 chr(10)

...产生LINEFEED字符(也称为转义序列\n),而psql用换行符显示该字符(由表示+)。那里一切都正确。

2.&3. ascii()产生128还是192?

它始于我犯的一个错误。我粗心大意地假定"char"会在引用的答案(现已修复)中覆盖一个无符号的 1字节整数(0到255)的范围,但是实际上它是内部一个有符号的 1字节整数(-128到127)的范围。

ascii()接受一个text参数,从隐式转换为"char"text以unicode产生一个多字节编码的字符,然后该函数返回(根据上的文档ascii()):

参数第一个字符的ASCII码。对于UTF8,返回字符的Unicode代码点。对于其他多字节编码,参数必须是ASCII字符。

因此,我们得到了很多截断的值。128和192是多字节字符前导字节的字节值。

4.空字节

由于无法存储空字节只影响普通字符类型(textcharvarchar),不是"char"。它适用于我的越野车示例,因为我将其投射text为垫脚石。虽然之间铸造"char"integer直接,限制不适用。手册chr()

不允许使用NULL(0)字符,因为文本数据类型无法存储此类字节。

对于“ char”则不是这样,其中0映射到空字符串''

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

请记住:"char"仍然是一种“内部”类型,旨在进行简单且廉价的枚举。没有为我们在此所做的工作而正式设计,并且不能移植到其他RDBMS。Postgres项目对此没有任何保证。


我仍然认为\ r显示的结果psql是错误或怪异的东西。它完成了该行,然后跳过了一行?
埃文·卡罗尔

4
@Evan不,它不会“跳过一行”,空白行是上一行的延续(多行)。如果您可以让psql在输出行之间绘制水平线,这将更加明显,但是因为您看不到视觉线索是'+'。
杰克说请尝试topanswers.xyz

0

要转换到有符号范围,您可以创建一些功能来帮助您。此列表将创建强制转换的函数,以帮助协助从无符号的一个单字节int范围[0-255]字符需要的[-128,127]符号的一个字节范围的过程

自述文件的摘录

现在,您可以执行将值存储[0-255]在表中的范围内。

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

将它们转换为 bit(8)

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

也许您想清除低两位,可以使用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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.