Go 基本数据

第 2 章 基本数据类型

Go 的数值类型包括了不同大小的整数 、浮点数 、复数;

各种数值类型分别有自己的大小,对正负号支持也各不相同;

   

1. 整数(OK)

整数类型(整型)
整数类型Go 语言同时支持 有符号整数无符号整数
有符号整数

有符号整数分 4 种大小:8 位 、16 位 、32 位 、64 位

int8int16int32int64
无符号整数

无符号整数也分 4 种大小:8 位 、16 位 、32 位 、64 位

uint8uint16uint32uint64

平台

默认整型

还有两种类型
intuint

在特定平台上,其大小与原生的有符号整数 \ 无符号整数相同,

或等于该平台上的运算效率最高的值

" int " 是目前使用最广泛的数值类型

" int " 和 " uint " 在同一台机器上的大小相等,要么都是 32 位,要么都是 64 位;

但不能认为一定就是 32 位,或一定就是 64 位;

即使在同样的硬件平台上,不同的编译器可能选用不同的大小

两个类型

同义词

" rune " 类型是 int32 类型的同义词

常用于指明一个值是 Unicode 码点(code point)

" byte " 类型是 uint8 类型的同义词

强调一个值是原始数据,而非量值

rune 和 int32 可以互换使用

byte 和 uint8 可以互换使用

特殊

无符号整数

还有一种无符号整数 " uintptr " ,其大小并不明确,但足以完整存放指针

" uintptr " 类型仅仅用于底层编程

例如,在 Go 程序与 C 程序库或操作系统的接口界面

类型有别

显式转换

" int " 、" uint " 、" uintptr " 都有别于其他大小明确的相似类型的类型;

也就是说," int " 和 " int32 " 是不同类型,尽管 " int " 天然的大小就是 32 位

如果 " int " 值要当做 " int32 " 来使用,必须显式转换;反之亦然

取值范围

有符号整数在机器中以 " 补码 " 表示,保留最高位作为符号位

n 位类型的取值范围是 -2^{n-1} ~ 2^{n-1} - 1

无符号整数由全部位构成非负数值;

n 位类型的取值范围是 0 ~ 2^n - 1

有符号

类型示例

有符号整型 int8 占 8 位,最高位是符号位,剩余 7 位表示数值;

表示范围是 -128 ~ 127

有符号类型的

三种表示

有符号整数用最高位作为符号位,0 代表正数,1 代表负数

此处用 int8 作为示例:

由于最高位作为符号位,只有 7 个位能用来表示数值

原码表示法:

0,000 0000 表示 +0

0,111 1111 表示 +127

1,000 0000 表示 -0

1,111 1111 表示 -127

反码表示法:

原码的符号位不变,剩余数值位,均取反;数值位进位,但不影响符号位

0,111 1111 表示 +0

0,000 0000 表示 +127

1,111 1111 表示 -0

1,000 0000  表示 -127

补码表示法:

符号位不变,反码加 1

0,111 1111 + 1 -> 0,000 0000  表示 +0 即为 0

0,000 0000 + 1 -> 0,000 0001 表示 +127

1,111 1111 + 1 -> 1,000 0000 表示 -0 ,不看符号位,值为 128 ,重定义为 -128

1,000 0000 + 1 -> 1,000 0001 表示 -127

从意义上看,-127 的二进制数减 1 后应该是 -128 ,也就是上面的 -0 ,

所以将 -0 定义为 -128

无符号

类型示例

无符号整型 uint8 占 8 位,全部位数用来表示数值

0000 0000 :0

1111 1111 : 255

运算符
二元运算符

Go 的二元操作符包括了 算术 、逻辑 、比较 等运算;

按优先级降序排列如下:左高右低,上高下低

运算符

优先级表

优先级降序序号值越小,优先级越高
1234567
*/%<<>>&&^
891011
+-|^
121314151617
==!=<<=>>=
18
&&
19
| |

说明

如上表所示,二元运算符分五大优先级;

同级别的运算符满足左结合律,为求清晰,可能需要圆括号来指定计算次序;

示例: mask & (1 << 28)

复合赋值

运算符

上述列表中前两行的运算符(如加法运算 +)都有对应的赋值运算符(如 +=),

用于简写赋值语句

运算符

应用场景

算术运算符 + 、- 、* 、/ 可用于整数 、浮点数 、复数
取模运算取模运算符 % 只能用于整数

取模运算符的行为因编程语言而异

在 Go 语言中,取模余数的符号总是与被除数保持一致

示例:

-5 % 3 = -1 ... -2

-5 % (-3)  = 1 ... -2

除法运算

除法运算( / )的行为取决于操作数是否都为整数

整数相除,商会舍弃小数部分

示例:

5.0 / 4.0 -> 1.25

5 / 4 -> 1

溢出

无论是有符号数还是无符号数,若表示算术运算结果所需的位超出该类型的范围,

就称为 "溢出"

溢出的高位部分会无提示地丢弃

假如原本的计算结果是有符号类型,且最左侧位是 1 ,则会形成负值,

以 int8 为例:

var u uint8 = 255

fmt.Ptintln(u, u+1, u*u)    // " 255  0  1 "

var i int8 = 127

fmt.Println(i, i+1, i*i)         // " 127  -128  1 "

比较运算

下列二元比较运算符用于比较两个类型相同的整数;

比较表达式本身的类型是布尔型

==等于
!=不等于
<小于
<=小于或等于
>大于
>=大于或等于

(i). 实际上,所有基本类型的值(布尔值 、数值 、字符串)都可以比较,

     这意味着两个相同类型的值,可以用 " == " 和 " != " 运算符比较

(ii). 整数 、浮点数 、字符串能根据比较运算进行排序

      许多其他类型的值是不可比较的,也无法排序

后面介绍每种类型时,将会分别说明比较规则

一元运算Go 语言支持一元加法和一元减法运算符
+一元取正(无实际影响)
-一元取负

对于整数,+x 是 0+x 的简写,而 -x 是 0-x 的简写;

对于浮点数和复数,+x 就是 x ,-x 是 x 的相反数

位运算Go 语言支持下列 " 位运算符 "
&位运算 "与" AND

对操作数的运算逐位独立进行,

不涉及算术进位或正负号

|位运算 "或" OR
^位运算 "异或" XOR
&^位清空(AND NOT)
<<左移
>>右移
异或 ^

(i).  如果作为二元运算符,运算符 " ^ " 表示按位 " 异或(XOR) " ,

      即两数相同返回 0 ,两数不同返回 1

(ii). 如果作为一元前缀运算符,运算符 " ^ " 表示按位取反或按位取补,

      运算结果就是操作数逐位取反

与反 &^

运算符 " &^ " 的作用是按位清除(AND NOT)

" &^ " 两侧操作数的对应位,先进行 " 与 " 操作,再执行 " 取反 " 操作

表达式  z = x &^ y 中,若 y 的某位是 1,则 z 的对应位等于 0 ;

否则,就等于 x 的对应位

位运算示例

下面的代码说明了如何用位运算,将一个 uint8 的值当作位集(bitset)处理,

其含有 8 个独立的位,高效且紧凑

Printf 用谓词 %b 以二进制形式输出数值,

副词 08 在这个输出结果前补零,补够 8 位

var  x  uint8  =  1  <<  1  |  1  <<  5

var  y  uint8  =  1  <<  1  |  1  <<  2

fmt.Printf("%08b\n",x)         // "00100010",集合 {1,5}

fmt.Printf("%08b\n",y)         // "00000110",集合 {1,2}

fmt.Printf("%08b\n",x&y)     // "00000010",交集 {1}

fmt.Printf("%08b\n",x|y)       // "00100110",并集 {1,2,5}

fmt.Printf("%08b\n",x^y)      // "00100100",对称差 {2,5}

fmt.Printf("%08b\n",x&^y)    // "00100000",差集 {5}

for i := uint(0);i < 8;i++ {

    if x&(1<<i)  !=  0 {    // 元素判定

        fmt.Printf(i)          // "1"  "5"

    }

}

fmt.Printf("%08b\n",x<<1)    // "01000100",集合 {2,6}

fmt.Printf("%08b\n",x>>1)    // "00010001",集合 {0,4}

左移 <<

右移 >>

在移位运算 x << n 和 x >> n 中:

(i). 操作数 n 决定操作数 x(二进制形式)左移/右移的位数,且 n 必须为无符号整型

(ii). 操作数 x 可以是有符号整型,也可以是无符号整型

(iii). 算术意义上 :

       左移运算 x << n 等价于 x * 2^n

       右移运算 x >> n 等价于 x / (2^n) ,最终结果舍弃小数部分

(iv). 左移用 0 填补右边空位;

       无符号整数右移用 0 填补左边空位,有符号整数右移用符号位的值填补左边空位

        注意:如果将整数以位模式进行处理,须使用无符号整型

说明

尽管 Go 具备无符号整型和相关算术运算,也尽管某些量值不可能为负,

但是我们往往采用有符号整型数,如数组的长度(尽管长度不可能为负)

示例

下面例子从后往前输出奖牌名称,循环里面用到了内置的 len 函数,

len 返回有符号整数

medals := []string{ "gold","silver","bronze" }

for  i  :=  len(medals) - 1;i  >=  0;i-- {

    fmt.Println(medals[i])    // "bronze","silver","gold"

}

无符号整型

运算易导致

严重错误

相反,假如 len 返回的结果是无符号整型,就会导致严重错误,

因为 i 随之也成为无符号整型,而根据定义,条件 i >= 0 将恒成立;

第 3 轮迭代后,有 i == 0 ,语句 i-- 使得 i 变成无符号整型的最大值,比如 2^{64} - 1

而非 -1 ;

导致 medals[i] 试图越界访问元素,超出 slice 范围,引发运行失败或宕机

Tips 

无符号整数往往只用于位运算符和特定算术运算符,如实现位集 、

解析二进制格式的文件 、散列 、加密

一般而言,无符号整数极少用于表示非负值

类型转换

通常,将某种类型的值转换成另一种类型,需要显示转换

算术和逻辑(不含移位)的二元运算符,其操作数的类型必须相同

虽然这样有时会导致表达式相对冗长,但是一个系列的错误得以避免,程序更易理解

示例

var  apples  int32  =  1

var  oranges  int16  =  2

var  compote  int  =  apples  +  oranges    // 编译错误:不同类型无法在一起运算

尝试编译这三个声明将产生错误消息:

非法操作:apples + oranges ( int32 与 int16 类型不匹配 )

类型不匹配(+ 的问题)有几种方法修正,最直接地,将全部操作数转换成同一类型:

var  compote  =  int(apples) + int(oranges)

类型转换

对于某种类型 T ,若允许转换,操作 T(x) 会将 x 的值转换成类型 T;

var x X = x1

var t T = t1

t = T(x)

很多整型 - 整型转换( int16 <-> int32 )不会引起值的变化,

仅告知编译器如何解读该值;

不过,缩减大小的整型转换( int64 -> int16 ),

以及整型与浮点型的相互转换( int64 <-> float64 ),

可能会改变值或损失精度

示例

f  :=  3.141  // a  float64

i  :=  int(f)

fmt.Println(f,i)    // " 3.141   3 "

f = 1.99

fmt.Println(int(f))    // " 1 "

类型转换

浮点型转换为整型,会舍弃小数部分,即趋零截尾(正值向下取整,负值向上取整),最终的结果相比之前,都更接近零

如果有些转换的操作数的值,超出了目标类型的表示范围 ( float64 -> int8 ),

就应当避免这种转换,因为其行为依赖于具体实现

示例

f  :=  1e100    // a float64

i  :=  int(f)       // 结果依赖于具体实现

整型

三种进制

无论有无大小和符号限制 :

(i).  源码中的整数都能写成常见的十进制数;

(ii). 也能写成八进制数,以 0 开头,如 0888

(iii). 还能写成十六进制数,以 0x 或 0X 开头,如 0xdeadBEEF

       十六进制数中的 x 或 a 到 f ,大小写皆可

(iv). 当前,八进制数似乎只有一种用途 -- 表示 POSIX 文件系统的权限

       而十六进制数,广泛用于强调其位模式,而非数值大小

示例

如下面的例子所示,如果使用 fmt 包输出数字,可以用谓词 %d 、%o 、%x 指定

进位制基数和输出格式:

o  :=  0666

fmt.Printf("%d  %[1]o  %#[1]o\n",o)    // " 438  666  0666 "

x  :=  int64(0xdeadbeef)

fmt.Printf("%d  %[1]x  %#[1]X\n",x)

// 输出:

// 3735928559  deadbeef  0xdeadbeef  0XDEADBEEF

说明

注意 fmt 的两个技巧:

(i). 通常 Printf 的格式化字符串含有多个 % 谓词,这要求提供相同数目的操作数,

     而 % 后面的副词 [1] 告诉 Printf 函数,重复使用第一个操作数

(ii). 其次,%o 、%x 或 %X 之前的副词 # ,则告诉函数 Printf 输出相应的前缀 0 、

      0x 、0X

说明

在源码中,文字符号(rune literal)的形式是字符写在一对单引号内;

最简单的例子就是 ASCII 码字符,如 'a' ,但也可以直接使用 Unicode 码点(codepoint)或码值转义

示例

用 %c 输出文字符号,如果希望输出带有单引号,则用 %q

ascii := 'a'

unicode := '国'

newline := '\n'

fmt.Printf("%d %[1]c %[1]q\n",ascii)         // " 97 a 'a' "

fmt.Printf("%d %[1]c %[1]q\n",unicode)    // " 22269 国 '国' "

fmt.Printf("%d %[1]q\n",newline)               // " 10 '\n' "

2. 浮点数

浮点数类型
两种浮点类型Go 语言支持两种大小的浮点数 :float32 和 float64
其算术特性遵循从 IEEE 754 标准,所有新式 CPU 都支持该标准

浮点型

表示范围

这两个类型的值可从极细微到超宏大

math 包给出了浮点值的极限;

常量 math.MaxFloat32 是 float32 的最大值,大约为 3.4e38,最小值为 1.4e-45

常量 math.MaxFloat64 是 float64 的最大值,大约为 1.8e308,最小值为 4.9e-324

十进制下,float32 的有效数字大约是 6 位,float64 的有效数字大约是 15 位;

绝大多数情况下,应优先选用 float64 ,因为除非格外小心,否则 float32 的运算

会迅速累积误差;

另外,float32 能精确表示的正整数范围有限:

示例

var  f  float32  =  16777216  // 1 << 24

fmt.Println( f  ==  f+1 )          // "true"

在源码中,浮点数可以写成小数,如:

const  e  =  2.71828  // ( 近似值 )

特别技巧

(i).  小数点前的数字可以省略( .707 即 0.707),小数点后面的也可以省略( 1. )

      省去的部分都为 0

(ii). 非常小或非常大的数字最好使用科学计数法表示,此方法在数量级指数前

      写字母 e 或 E

      const  Avogadro  =  6.02214129e23

      const  Planck  =  6.62606957e-34

浮点数的

格式化输出

浮点值能方便地通过 Printf 的谓词 %g 输出,该谓词会自动保持足够的精度,

并选择最简洁的表示方式,但是对于数据表,%e(有指数)或 %f(无指数)的形式可能更合适。

这三个谓词( %g 、%e 、%f )都能掌控输出宽度和数值精度

%n.xf  n 表示输出一个占多少个位的长度,x 表示小数点后保留多少位

示例

for  x  :=  0;x  <  8;x++ {

        fmt.Ptintf("x = %d e^x = %8.3f",x,math.Exp(float64(x)))

}

上面的代码按 8 个字符的宽度输出自然对数 e 的各个幂方,结果保留三位小数

x = 0  e^x =      1.000

x = 1  e^x =      2.718

x = 2  e^x =      7.389

x = 3  e^x =     20.086

x = 4  e^x =     54.598

x = 5  e^x =   148.413

x = 6  e^x =   403.429

x = 7  e^x =  1096.633

特殊值

除了大量常见的数学函数之外,math 包还有一些函数,用于创建和判断 IEEE 754 标准定义的特殊值:

正无穷大和负无穷大,表示超出最大许可值的数 、以及除以零的商

NaN(Not a Number),它表示数学上无意义的运算结果(如 0/0 或 Sqrt(-1) )

math.IsNaN 函数判断其参数是否为非数值,math.Nan 函数则返回非数值( NaN )

在数字运算中,我们倾向于将 NaN 当做信号值( sentinel value ),但直接判断具体的计算结果是否为 NaN 可能导致潜在错误,因为与 NaN 的比较总不成立(除了 != ,它总是与 ==  相反)

示例

nan  :=  math.Nan()

fmt.Println(nan == nan,nan < nan,nan > nan)  // "false  false  false"

特别注意一个函数的返回值是浮点型且它有可能出错,那么最好单独报错,如下:
示例

func compute() (value float64,ok bool) {

    // ...

    if failed {

        return 0,false

    }

    return result,true

}

下面的程序以浮点绘图运算为例;它根据传入两个参数的函数 z = f(x,y),绘出

三维的网线状曲面,绘制过程中运用了可缩放矢量图形(Scalabe Vector Graphics,SVG),绘制线条的一种标准 XML 格式;图 3-1 是函数 sin(r)/r 的图形输出样例,

其中 r 为 sqrt(x*x + y*y)

代码

// surface 函数根据一个三维曲面函数计算并生成 SVG

package main

import (

    "fmt"

    "math"

)

const (

    width,height = 600,320                  //  以像素表示的画布大小

    cells                = 100                            //  网格单元的个数

    xyrange           = 30.0                          //  坐标轴的范围(-xyrange ~ +xyrange)

    xyscale            = width / 2 / xyrange    // x 或 y 轴上每个单位长度的像素

    zscale              = height * 0.4               //  z 轴上每个单位长度的像素

    angle                = math.Pi / 6               //  x 、y 轴的角度( =30 )

)

var  sin30,cos30 = math.Sin(angle),math.Cos(angel)  // sin(30),cos(30)

func main() {

    

3. 复数(OK)

复数类型
两种复数类型

Go 语言中内置了两种大小的复数 complex64 和 complex128 ,

两者分别由 float32 和 float64 构成

complex内置的 complex 函数,根据给定的实部和虚部创建复数
real 、imag内置的 real 函数和 imag 函数,则分别提取复数的实部和虚部
示例

var x complex128 = complex(1,2)    // 1+2i

var y complex128 = complex(3,4)    // 3+4i

fmt.Println(x*y)                                    // "(-5+10i)"

fmt.Println(real(x*y))                            // "-5"

fmt.Println(imag(x*y))                          // "10"

虚数

如果在浮点数或十进制整数后面紧接着字母 " i " ,如 3.1415926i 或 2i ,

它就变成了一个虚数,表示一个实部为 0 的复数

fmt.Println( 1i * 1i )    //  " (-1 + 0i) "  ,i^2 = -1
复数常量根据常量运算规则,复数常量可以和其他常量相加(整型或浮点型,实数和虚数皆可)

我们可以自然地写出复数,如 1+2i ,或等价地,2i+1

前面 x 和 y 的声明可以简写为:

x := 1 + 2i

y := 3 + 4i

相等性比较

可以用 " == " 或 " != " 判断两个复数是否相等;

若两个复数的实部相等且虚部相等,则这两个复数相等

库扩展

math / cmplx 包提供了复数运算所需的库函数;

例如,复数的平方根函数 、复数的幂函数

示例:下面的程序用 complex128 运算生成一个 Mandelbrot 集

// madelbrot 函数生成一个 PNG 格式的 Mandelbrot 分形图
package mainimport ("image""image/color""image/png""math/cmplx""os"
)func main() {const (xmin, ymin, xmax, ymax = -2, -2, +2, +2width, height          = 1024, 1024)img := image.NewRGBA(image.Rect(0, 0, width, height))for py := 0; py < height; py++ {y := float64(py)/height * (ymax-ymin) + yminfor px := 0; px < width; px++ {x := float64(px)/width * (xmax-xmin) + xminz := complex(x, y)// 点 (px, py) 表示复数数值 zimg.Set(px, py, mandelbrot(z))}}png.Encode(os.Stdout, img)
}func mandelbrot(z complex128) color.Color {const iterations = 200const contrast = 15var v complex128for n := uint8(0); n < iterations; n++ {v = v*v + zif cmplx.Abs(v) > 2 {return color.Gray(255 - contrast*n)}}return color.Black
}

4. 布尔值(OK)

布尔类型

布尔类型声明

var a bool

var b bool = false

取值范围true(真)或 false(假)
使用场景

if 和 for 语句中的条件就是布尔值,

比较操作符(如 == 和 < )也能得出布尔值结果

取反操作

一元操作符(!)表示逻辑取反,因此 !true 就是 false ,

或者 (!true == false) == true

判断简写

考虑到代码风格,布尔表达式 x == true 相对冗长,简化为 x

if (x == true) 替换为 if (x) ,其中 x 是布尔类型

短路概念

布尔值可以由运算符 &&(AND)以及 ||(OR)组合运算,这会引起短路行为:

如果逻辑运算符左侧的表达式已经能确定最终结果,

那么逻辑运算符右侧的表达式不会被再次计算

逻辑运算规则

&&(与)运算规则:只要有一个表达式结果为假,则最终的结果就是假

|| (或)运算规则:只要有一个表达式结果为真,则最终的结果就是真

逻辑运算示例

下面的表达式是安全的:

s != "" && s[0] == 'x'

若字符串 s 为空字符串,则 s != "" 表达式返回 false ,

则不会再判断 s[0] == 'x'

若 s 为空字符串,像 s[0] == 'x' 这样使用 s[0] ,会触发宕机异常

优先级&& 较 || 优先级更高(助记窍门:&& 表示逻辑乘法,|| 表示逻辑加法)
优先级示例

所以如下形式的条件组合无须加圆括号:

if 'a' <= c && c <= 'z' ||

   'A' <= c && c <= 'Z' ||

   '0' <= c && c <= '9' {

   // ...

}

零值布尔类型的零值(默认值)为 false

布尔类型无法与

其他类型相互转换

布尔值无法隐式转换成数值(如 true 转化为 1 ,false 转换为 0);

数值也无法隐式转换成布尔值(如 0 转换为 false ,非 0 数如 5 转换为 true)

总结:布尔型无法参与数值运算

不允许强制将数值类型转换为布尔型,不允许强制将布尔型转换为数值类型
布尔型无法与其他类型相互转换
简单解决办法

i := 0

if b {

    i = 1

}

说明:用变量 i 的值代替布尔变量 b 的值

布尔变量 b 不能参与数值运算,根据 b 的 true 还是 false 决定 i 的值为 1 还是 0

让整型变量 i 参与数值运算

专用转换函数

假如转换操作常常会用到,那就应该专门为此写个函数

// 如果 b 为真,btoi 返回 1 ;如果 b 为假,btoi 返回 0

// 布尔值转数值

func btoi(b bool) int {

    if b {

        return 1

    }

    return 0

}

// 数值转布尔值

func itob(i int) bool { return i != 0 }

5. 字符串

字符串
字符串概念

字符串是不可变的字节序列,它可以包含任意数据,包括 0 值字节,

但主要是人类可读的文本

习惯上,文本字符串被解读成按 UTF-8 编码的 Unicode 码点(文字符号)序列

字符串操作内置的 len 函数返回字符串的字节数并非文字符号的数目
下标访问操作 s[i] 则取得第 i 个字符,其中 0 \leq i \leq len(s)
示例

s  :=  "hello,world"

fmt.Println(len(s))          //  " 12 "

fmt.Println(s[0],s[7])    //  " 104  119 "  ('h' and 'w')

越界访问

触发宕机

试图访问许可范围以外的字节,会触发宕机异常
示例c  :=  s[len(s)]    // 宕机 :下标越界
特别注意

字符串的第 i 个字节,不一定就是第 i 个字符,

因为非 ASCII 字符的 UTF-8 码点需要两个字节或多个字节

提取子串

切片操作

子串生成操作 s[i:j] 产生一个新字符串,内容取自原字符串的字节,下标从 i(含边界值)开始,直到 j(不含边界值),即 i\leq n < j ;子串的大小是 j-i 个字节
示例fmt.Println(s[0:5])    // "hello"
再次强调若下标越界,或者 j 的值小于 i ,将触发宕机异常

子串默认

起始位置

操作数 i 与 j 的默认值分别是 0(字符串起始位置)和 len(s)(字符串终止位置),

若省略 i 或 j ,或两者都省略,则取默认值

示例

fmt.Println(s[:5])    // "hello"

fmt.Println(s[7:])    // "world"

fmt.Println(s[:])      // "hello,world"

字符串连接加号( + )运算符,连接两个字符串而生成一个新字符串
示例fmt.Println("goodbye" + s[5:])  // "goodbye,world"
字符串比较

字符串可以通过比较运算符,来比较两个字符串,如 " == " 和 " < " ;

比较运算按字节进行比较,结果服从本身的字典排序

字符串

不可改变

尽管肯定可以将新值赋予字符串变量,但是字符串值无法改变:

字符串值本身所包含的字节序列永远不会变(参考 python 字符串性质)

因为字符串不可改变,所以字符串内部的数据不允许修改:

s[0]  =  'L'     //  编译错误:s[0] 无法赋值

字符串

追加

要在一个字符串后面添加另一个字符串,可以像下面这样编写代码:

s  :=  "left foot"

t  :=  s

s  +=  ",right foot"

上面这样的操作,不会改变 s 原有的字符串值,只是将 " += " 语句生成的新字符串

赋值给 s ;同时,t 仍然持有旧的字符串值

fmt.Println(s)  // " left foot,right foot "

fmt.Println(t)  // " left foot "

共用

底层内存

不可改变意味着两个字符串能安全地共用同一段底层内存,使得复制任何长度字符串

的开销都低廉;类似地,字符串 s 及其子串(如 s[7:] )可以安全地共用数据,因此,

子串生成操作的开销也低廉;这两种情况(复制字符串 、提取子串)都没有分配内存

3.5.1 字符串字面量

字符串

字面量

字符串的值,可以直接写成字符串字面量(string literal),形式上就是:

带双引号的字节序列

示例"Hello,世界"
任意字符

因为 Go 的源文件总是按 UTF-8 编码,并且习惯上 Go 的字符串会按 UTF-8 解读,

所以在源码中,可以将 Unicode 码点写入字符串字面量

var s string = "我们のworld"

转义字符

在带双引号的字符串字面量中,转义序列以反斜杠( \ )开始,可以将任意值的字节

插入字符串中;

下面是一组转义符,表示 ASCII 控制码,如换行符 、回车符 、制表符

\a   "警告" 或响铃

\b   退格符

\f    换页符

\n    换行符(直接跳到下一行的同一位置)

\r    回车符(返回行首)

\t    制表符

\v   垂直制表符

\'    单引号(仅用于文字字符字面量 ' \' ')

\"   双引号(仅用于 "..." 字面量内部)

\\    反斜杠

其他进制

数字字节

源码中的字符串也可以包含十六进制或八进制的任意字节

十六进制的转义字符写成  ' \xhh ' 的形式,' h ' 是十六进制数字(大小写皆可以),

且必须是两位

八进制的转义字符写成 ' \ooo ' 的形式,必须使用三位八进制数字(0 ~ 7),

且不能超过 '\377 '

这两者都表示单个字节,内容是给定值
原生字符串

原生的字符串字面量的书写形式是 `...` ,使用反引号而不是双引号;

原生的字符串字面量中,转义序列不起作用

(i).  也就是说,实质内容与字面写法严格一致,包括反斜杠和换行符

(ii). 因此,在程序源码中,原生的字符串字面量可以展开多行

(iii). 唯一的特殊处理是回车符会被删除(换行符会保留),使得同一字符串在所有

       平台上的值都相同,包括习惯在文本文件存入换行符的系统

使用场景

正则表达式往往含有大量反斜杠,可以方便地写成原生的字符串字面量;

原生的字符串字面量也适用于 HTML 模板 、JSON 字面量 、命令行提示信息 ,

以及需要多行文本表达的场景

多行文本

const   GoUsage  =  `Go is a tool managing Go source code.

Usage:

         go command [arguments]

...`

3.5.2 Unicode
背景与问题

此前,软件只须处理一个字符集:ASCII(美国信息交换标准码)

ASCII( 或更确切地说,US-ASCII )码使用 7 位来表示 128 个 "字符" :

大小写英文字母 、数字 、各种标点 、设备控制符

这对早期的计算机行业已经足够了,但是让世界上众多使用其他语言的人无法在计算机

上使用自己的文字书写体系;随着互联网的兴起,包含纷繁语言的数据屡见不鲜;

到底怎样才能应付语言的繁杂多样,还能兼顾效率?

解决方法

新问题

答案是 Unicode(unicode.org),它囊括了世界上所有文字书写体系的全部字符,还有重音符和其他变音符,控制码(如制表符和回车符),以及许多特有文字,对它们各自赋予一个叫 Unicode 码点的标准数字;

在 Go 的术语中,这些字符记号称为文字符号( rune )

Unicode 第 8 版定义了超过一百种语言文字的 12 万个字符的码点;

它们在计算机程序和数据中如何表示?天然适合保存单个文字符号的数据类型就是 int32 ,被 Go 所采用;正因如此,runne 类型作为了 int32 类型的别名

我们可以将文字符号的序列表示成 int32 值序列,这种表示方式称作 UTF-32 或

UCS-4 ,每个 Unicode 码点的编码长度相同,都是 32 位;这种编码简单划一,

可能因为大多数面向计算机的可读文本是 ASCII 码,每个字符只需 8 位,也就是 1 字节,导致了不必要的存储空间的消耗;

而广泛使用的字符数目也少于 65536 个,字符用 16 位就能容纳,我们能做改进吗?

3.5.3 UTF-8

UTF-8

编码

UTF-8 以字节为单位对 Unicode 码点作变长编码

UTF-8 是现行的一种 Unicode 标准,

由 Go 的两位创建者 Ken Thompson 和 Rob Pike 发明

每个文字符号用 1 ~ 4 个字节表示,ASCII 字符的编码仅占 1 个字节,

而其他常用的文字字符的编码只占 2 或 3 个字节

一个文字符号编码的首字母的高位,指明了后面还有多少字节;

(i).  若最高位为 0 ,则标示着它是 7 位的 ASCII 码,其文字符号的编码仅占 1 字节,这样就与传统的 ASCII 码一致

(ii). 若最高几位是 110 ,则文字符号的编码占用 2 个字节,第二个字节以 10 开始;

      更长的编码以此类推

0xxxxxxx                                                   文字符号 0 ~ 127(ASCII)

110xxxxx 10xxxxxx                                   128 ~ 2047            少于 128 个未使用的值

1110xxxx 10xxxxxx 10xxxxxx                   2048 ~ 65535      少于 2048 个未使用的值

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx   65536 ~ 0x10ffff   其他未使用的值

缺点变长编码的字符串无法按下标直接访问第 n 个字符
优点

然而有失有得,UTF-8 换来许多有用的特性:

(i).  UTF-8 编码紧凑,兼容 ASCII 

(ii). 自同步 :最多追溯 3 个字节,就能定位一个字符的起始位置

(iii). 前缀编码,能从左向右编码而不产生歧义,也无须超前预读;

       于是查找文字符号仅需搜索它自身的字节,不必考虑前文内容

(iv). 文字符号的字典字节顺序与 Unicode 码点顺序一致(Unicode 设计如此),

       因此按 UTF-8 编码排序自然就是对文字符号排序

(v). UTF-8 编码本身不会嵌入 NUL 字节( 0 值 ),这便于某些程序语言用 NUL 

      标记字符串结尾

Go 对 UTF-8

的支持

Go 的源文件总是以 UTF-8 编码,同时,需要用 Go 程序操作的文本字符串也优先采用

UTF-8 编码;

unicode 包具备针对单个文字符号的函数(例如区分字母和数字,转换大小写),

而 unicode/utf8 包,则提供了按 UTF-8 编码和解码文字符号的函数

用码点表示

Unicode字符

许多 Unicode 字符难以直接从键盘输入;有的看起来十分相似几乎无法辨认;

有些甚至不可见

在 Go 语言中,字符串字面量的转义让我们得以用码点的值来指明 Unicode 字符;

有两种形式 :

(i).  \uhhhh 表示 16 位码点值

(ii). \uhhhhhhhh 表示 32 位码点值

其中,每个 h 代表一个十六进制数字;32 位形式的码点值几乎不需要用到

这两种形式都以 UTF-8 编码表示出给定的码点

示例

因此,下面几个字符串字面量都表示长度为 6 个字节的相同串:

"世界"

"\xe4\xb7\x96\xe7\x95\x8c"

"\u4e16\u754c"

"\U00004e16\U0000754c"

后面三行的转义序列用不同形式表示第一行的字符串,但实质上它们的字符串值都一样

Unicode 转义符也能用于文字符号;下列字符是等价的:

'世'     '\u4e16'      '\U00004e16'

特别注意

码点值小于 256 的文字符号可以写成单个十六进制数转义的形式,如 'A' 写成 '\x41' ,

而更高的码点值则必须使用 \u 或 \U 转义

这就导致,'\xe4\xb8\x96' 不是合法的文字符号,虽然这三个字节构成某个有效的

UTF-8 编码码点

UTF-8 的

操作示例

由于 UTF-8 的优良特性,许多字符串操作都无须解码;

// 我们可以直接判断某个字符串是否为另一个的前缀:

func HasPrefix( s ,prefix  string ) bool {

        return len(s) >= len(prefix) && s[:len(prefix)] == prefix

}

// 是否为另一个字符串的后缀

func HasSuffix( s ,suffix  string ) bool {

        return len(s) >= len(suffix) && s[len(s) - len(suffix) :] == suffix

}

// 是否为另一个字符串的子串

func Contains( s ,substr  string ) bool {

    for  i := 0;i < len(s);i++ {

        if HasPrefix( s[i:] ,substr ) {

            return true

        }

    }

    return false

}

    

说明

按 UTF-8 编码的文本的逻辑同样也适用于原生字节序列,但其他编码则无法如此;

(上面的函数取自 strings 包,其实 Contains 函数的具体实现,使用了散列方法,让搜索更高效)

另一方面,如果我们真的要逐个逐个处理 Unicode 字符,则必须使用其他编码机制;

考虑到我们第一个例子的字符串 "世界" ,该字符串包含两个中文字符;

图 3-5 说明了该字符串的内存布局;

它含有 13 个字节,而按作 UTF-8 解读,本质是 9 个码点或文字符号的编码:

import  "unicode/utf8"

s := "Hello,世界"

fmt.Println(len(s))                                   // "13"

fmt.Println(utf8.RuneCountInString(s))  // "9"

我们需要 UTF-8 解码器来处理这些字符,unicode/utf8 包就具备一个:

for  i := 0;i < len(s);{

    r,size := utf8.DecodeEuneInString(s[i:])

    fmt.Printf("%d\t%c\n",i,r)

    i += size

}

说明:

每次 DecodeRuneInString 的调用都返回 r(文字符号本身)

和一个值 offset(表示 r 按 UTF-8 编码所占用的字节数)

offset 这个值用来更新下标 i ,定位字符串内下一个文字符号;

可是按此方法,我们总是需要使用上例中的循环方式;

所幸,Go 的 range 循环也适用于字符串,按 UTF-8 隐式解码;

图 3-5 也展示了以下循环的输出;注意,对于非 ASCII 文字符号,下标增量大于 1

for  i,r  :=  range  "Hello,世界"  {

    fmt.Printf("%d\t%q\t%d\n",i,r,r)

}

// 我们可以用简单的 range 循环统计字符串中的文字符号数目,如下所示

n  :=  0

for _,_   =  range  s  {

    n++

}

// 与其他形式的 range 循环一样,可以忽略没用的变量

n  :=  0

for  range  s  {

    n++

}

// 或者,直截了当地调用 utf8.RuneCountInString(s)

之前提到过,文本字符串作为按 UTF-8 编码的 Unicode 码点序列解读,很大程度上是出于习惯,但为了确保使用 range 循环能正确处理字符串,则必须要求而不仅仅是按照习惯;如果字符串含有任意二进制数,也就是说,UTF-8 数据出错,而我们对它做 range 循环,会发生什么?

每次 UTF-8 解码器读入一个不合理的字节,无论是显式调用 utf8.DecodeRuneInString ,还是在 range 循环内隐式读取,都会产生一个专门的 Unicode 字符 '\uFFFD' 替换它,其输出通常是个黑色六角形或类似钻石的形状,里面有个白色问号;

如果程序碰到这个文字符号值,通常意味着,生成字符串数据的系统上游部分在处理文本编码方面存在瑕疵

UTF-8 是一种分外便捷的交互格式,而在程序内部使用文字字符类型可能更加方便,因为它们大小一致,便于在数组和 slice 中用下标访问
当 []rune 转换作用于 UTF-8 编码的字符串时,返回该字符串的 Unicode 码点序列:

// 日语片假名 "程序"

s  :=  "プログラム"

fmt.Printf("% x\n",s)  // "e3 83 97 e3 83 ad e3 82 b0 e3 83 a9 e3 83 a0"

r  :=  []rune(s)

fmt.Printf("%x\n",r)  //  "[30d7  30ed  30b0  30e9  30e0]"

(第一个 Printf 里的谓词 %x (注意,% 和 x 之间有空格)以十六进制数形式输出,并在每两个数位间插入空格)

如果把文字符号类型的 slice 转换成一个字符串,它会输出各个文字符号的 UTF-8 编码拼接结果:

fmt.Println(string(r))  "プログラム"

若将一个整数值转换成字符串,其值按文字符号类型解读,并且产生代表该文字符号值的 UTF-8 码:

fmt.Println(string(65))           // "A",而不是 "65"

fmt.Println(string(0x4eac))    // "京"

如果文字符号值非法,将被专门的替换字符取代(见前面的 '\uFFFD')

fmt.Println(string(1234567))    // "�"

3.5.4 字符串和字节 slice

4 个标准

字符串包

4 个标准包对字符串操作特别重要:strings 、bytes 、strconv 、unicode
strings 包strings 包提供了许多函数,用于搜索 、替换 、比较 、修整 、切分 、连接字符串
bytes 包bytes 包也有 strings 包中类似的函数,用于操作字节 slice( []byte 类型,其某些属性和字符串相同 );由于字符串不可变,因此按增量方式构建字符串会导致多次内存分配和复制;这种情况下,使用 bytes.Buffer 类型会更高效
strconv 包strconv 包具备的函数,主要用于转换布尔值 、整数 、浮点数 为与之对应的字符串形式,或者把字符串转换为 布尔值 、整数 、浮点数,另外还有为字符串添加 / 去除引号的函数
unicode 包

unicode 包备有判别文字符号值特性的函数,如 IsDigit、IsLetter、IsUpper 、IsLower 

每个函数以单个文字符号值作为参数,并返回布尔值;

若文字符号值是英文字母,转换函数(如 ToUpper 和 ToLower)将其转换成指定的大小写;

上面所有函数都遵循 Unicode 标准,对字母数字等的分类原则;

strings 包也有类似的函数,函数名也是 ToUpper 和 ToLower ,它们对原字符串的每个字符做指定变换,生成并返回一个新字符串

下例中,basename 函数模仿 UNIX shell 中的同名实用程序;只要 s 的前缀看起来像是文件系统路径(各部分由斜杠分隔),该版本的 basename(s) 就将其移除,貌似文件类型的后缀也被移除: 

fmt.Println(basename("a/b/c.go"))  // "c"

fmt.Println(basename("c.d.go"))     // "c.d"

fmt.Println(basename("abc"))         // "abc"

初版的 basename 独自完成全部工作,并不依赖任何库:

// gop.io/ch3/basename1

// basename 移除路径部分和 . 后缀

// e.g. ,a => a ,a.go => a , a/b/c.go => c ,a/b.c.go => b.c

func basename( s string ) string {

    // 

}

6. 常量

常量是一种表达式,其可以保证在编译阶段就计算出表达式的值
所有常量本质上都属于基本类型,如 :布尔型 、字符串 、数字

常量的声明定义了具名的值(命名对象,其值一旦确定,就不可修改),语法上与变量类似,防止了程序运行过程中的意外(或恶意)修改

例如,姚表示数学常量,像圆周率,在 Go 程序中用常量表示比变量更适合,因为值是恒定不变的

常量声明

声明单个常量

const pi = 3.1415926    // 近似数;math.Pi 是更精准的近似

声明一组常量

与变量类似,同一个声明可以定义一系列常量,适用于一组相关的值;

const (

    e = 2.718281828

    pi = 3.1415926

)

为什么使用常量?

许多针对常量的计算完全可以在编译阶段完成,从而减少运行时的工作量,并让其他编译器优化得以实现;

某些错误通常要在运行时才能检测到,但如果操作数是常量,编译时就会报错,例如整数除以 0 ,字符串下标越界,以及任何产生无限大值的浮点数运算

1. 对于常量操作数,所有数学运算 、逻辑运算 、比较运算的结果依旧是常量;

2. 常量的类型转换结果还是常量;

3. 某些内置函数的返回值是常量,如 len 、cap 、real 、imag 、complex 、unsafe.Sizeof 等

因为编译器知道常量(表达式)的值,所以常量表达式可以出现在涉及类型的声明中,主要就是数组类型的长度

const IPv4Len = 4

// parseIPv4 函数解析一个 IPv4 地址(d.d.d.d)

func parseIPv4(s string) IP {

    var p [IPv4Len]byte

    // ...

}

常量声明可以同时指定类型和值,如果没有显式指定类型,则类型根据右边的表达式推断(有点类似使用 var 关键字声明变量)

var a int = 5

var b int

var c = 10

fmt.Println(b)    // " 0 "

fmt.Printf("%T\n", c)  // " int "

下面的例子中,time.Duration 是一种具名类型,其基本类型是 int64 ,time.Minute 也是基于 int64 的常量;下面声明的两个常量都属于 time.Duration 类型,通过 %T 展示

const noDelay time.Duration = 0

const timeout = 5 * time.Minute

fmt.Printf("%T %[1]v\n",noDelay)        // "time.Duration 0"

fmt.Printf("%T %[1]v\n",timeout)          // "time.Duration 5m0s"

fmt.Printf("%T %[1]v\n",time.Minute)    // "time.Duration 10m0s"

若同时声明一组常量,除了第一项之外,其他项在等号右侧的表达式都可以省略;

这意味着,会复用前一项的表达式及其类型

例如:

const (

    a = 1

    b

    c = 3.14

    d

)

fmt.Println(a,b,c,d)    // "1 1 3.14  3.14"

如果复用右侧表达式导致计算结果总是相同,这就不太实用;假若该结果可变,该怎么办呢?来看看 iota

常量生成器 iota
iota

常量的声明可以使用常量生成器 iota ,它创建一系列相关值,而不是逐个值显式写出;

常量声明中,iota 从 0 开始取值,逐项加 1

iota 的理解

1. iota 遇到 const ,就会被重置为 0

2. 每出现一个新的常量项,iota 的值都自动加 1

示例

下面的例子来自 time 包,它定义了 Weekday 的具名类型,并声明每周的 7 天为该类型的常量,从 Sunday 开始,其值为 0

这种类型通常称为 "枚举型(enumeration ,或缩写为 enum)"

type  Weekday  int

const (

    Sunday Weekday = iota  // Sunday为常量项,Weekday为类型,iota 是初始值,为 0

    Monday                           // iota = 1

    Tuesday                          // iota = 2

    Wednesday                     // iota = 3

    Thurday                           // iota = 4

    Friday                              // iota = 5

    Saturday                          // iota = 6

)

更复杂的表达式也可使用 iota ,借用 net 包的代码举例如下,无符号整数最低 5 位数中的每一个都逐一命名,并解释为布尔值

type  Flags  uint

const (

    FlagUp Flags = 1 << iota    // 向上

    FlagBroadcast                    // 支持广播访问

    FlagLoopback                     // 环回接口

    FlagPointToPoint                // 点对点链路

    FlagMulticast                      // 支持多路广播访问

)

随着 iota 递增,每个常量项都按 1<< iota 赋值,这等价于 2 的连续次幂,它们分别与单个 "位" 对应;若某些函数要针对相应的位执行判定 、设置 或 清除操作,就会用到这些常量

func IsUp(v Flags)  bool          { return v&FlagUp == FlagUp }

func TurnDown(v *Flags)         { *v &^= FlagUp }

func SetBroadcast(v *Flags)    { *v |= FlagBroadcast }

func IsCast(v Flags)  bool        {  return v&( FlagBroadcast | FlagMulticast) != 0 }

func main() {

    var v Flags = FlagMulticast | FlagUp

    fmt.Printf("%b  %t\n",v,IsUp(v))       // "10001  true"

    turnDown(&v)

    fmt.Printf("%b  %t\n",v,IsUp(v))       // "10000  false"

    SetBroadcast(&v)

    fmt.Printf("%b  %t\n",v,IsUp(v))       // "10010  false"

    fmt.Printf("%b  %t\n",v,IsCast(v))    // "10010  true"

}

下面的例子更复杂,声明的常量表示 1024 的幂

const (

    _ = 1 << ( 10 * iota )

    KiB     // 1024

    MiB    //  1048576

    GiB    //   1073741824

    TiB     //   1099511627776

    PiB     //   1125899906842624

    EiB     //   1152921504606846976

    ZiB     //   1180591620717411303424

    YiB     //   12089258196146291774706176

)

然而,iota 机制也存在局限;

比如,因为不存在指数运算符,所以无法生成更为人熟知的 1000 的幂(KB 、MB 等)

总结

(特别重要)

1. 同时声明一组常量,除了第一个常量项之外,后续常量项在等号右侧的表达式都可以省略;这意味着,会复用前一项的表达式及其类型

2. iota 遇到 const ,就会被重置为 0

3. 每出现一个新的常量项,iota 的值都自动加 1

无类型常量

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/643863.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Servlet 与 MVC

主要内容 Servlet 重点 MVC 重点 Filter 重点 章节目标 掌握 Servlet 的作用 掌握 Servlet 的生命周期 掌握 JSP 的本质 掌握 MVC 的设计思想 掌握 Filter 的作用及使用场景 第一节 Servlet 1. Servlet 概念 Servlet 是在服务器上运行的能够对客户端请求进行处理&a…

Parallels Desktop 18 for Mac(pd虚拟机) 激活版

Parallels Desktop 18是一款功能强大的虚拟机软件&#xff0c;可以在Mac操作系统上同时运行多种操作系统&#xff0c;包括Windows、Linux、Android等。该软件提供了多种高级功能&#xff0c;如支持DirectX 11游戏、3D图形和OpenGL应用程序&#xff0c;以及运行Windows和Mac应用…

Mysql数据库DQL查询语言之表连接(联合查询)

表连接 关系字段&#xff1a;两表中有关联关系的字段 \关系字段&#xff1a;两表之间存在关系的字段 什么是表连接&#xff1f; 当我们的查询结果需要从多张表中获取时&#xff0c;此时应该让表之间建立连接&#xff0c;同时获取数据 内连接 特点&#xff1a;同时对连接双方做…

[BUUCTF]-PWN:babyfengshui_33c3_2016解析

又是一道堆题&#xff0c;先看保护 关键信息是32位&#xff0c;没开pie 直接看ida 大致是alloc创建堆块&#xff0c;free释放堆块&#xff0c;show查看堆块内容&#xff0c;fill填充堆块内容 其他的都没啥关键的要讲&#xff0c;但alloc那里非常需要解析一下 解释如上图 再具…

做外贸指定货代,想不到的麻烦

最近在外贸群里看到小伙伴们对指定货代的吐槽以及自己遇到的糟心事&#xff0c;写下来&#xff0c;也为我们今后遇到客户指定货代的时候提个醒。 小伙伴A说&#xff0c;因为客户设计的包装有问题&#xff0c;被海关查验&#xff0c;然后客户的指定货代要求小伙伴支付高于货值的…

向量库与嵌入模型

简介 非结构化数据 世界上大约超过百分之八十的数据都是非结构化数据&#xff0c;例如&#xff1a;图像、音视频、自然语言等&#xff0c;这些模型不遵循预定义的模式或组织方式&#xff0c;可以使用各种人工智能 (AI) 和机器学习 (ML) 模型转换为向量。 嵌入向量&#xff0…

用ChatGPT教学、科研!大学与OpenAI合作

亚利桑那州立大学&#xff08;简称“ASU”&#xff09;在官网宣布与OpenAI达成技术合作。从2024年2月份开始&#xff0c;为所有学生提供ChatGPT企业版访问权限&#xff0c;主要用于学习、课程作业和学术研究等。 为了帮助学生更好地学习ChatGPT和大语言模型产品&#xff0c;AS…

01.Elasticsearch应用(一)

Elasticsearch应用&#xff08;一&#xff09; 1.什么是ELK ELK是一个免费开源的日志分析架构技术栈总称&#xff0c;包含三大基础组件&#xff0c;分别是Elasticsearch、Logstash、Kibana。但实际上ELK不仅仅适用于日志分析&#xff0c;它还可以支持其它任何数据搜索、分析和…

nodejs学习计划--(六)包管理工具

包管理工具 1. 介绍 包是什么 『包』英文单词是 package &#xff0c;代表了一组特定功能的源码集合包管理工具 管理『包』的应用软件&#xff0c;可以对「包」进行 下载安装 &#xff0c; 更新 &#xff0c; 删除 &#xff0c; 上传 等操作 借助包管理工具&#xff0c;可以快…

【若依】前后端分离框架部署

1.拉取若依项目代码 进入若依 基于SpringBootVue前后端分离的Java快速开发框架&#xff0c;并通过相应协议拉取到本地IDE 2.前端部署 2.1.下载nodejs 进入nodejs官网下载所需版本&#xff0c; 完成后在命令行运行npm -v可查询版本 2.2.配置依赖 在若依ui目录下运行 np…

oracle古法unwrap手艺(oracle存储过程解码)

先说骚话 首先oracle官方是不支持解包的&#xff0c;见Doc ID 376303.1 但是需求来了。我就寄希望于民间大神的工具。很顺利&#xff0c;找到了几个&#xff0c;甚至还有网页版&#xff0c;以为是个easy money。 但是&#xff0c;我点背&#xff0c;总是能遇到精彩的情况。数…

Android开发--状态栏布局隐藏的方法

1.问题如下&#xff0c;安卓布局很不协调 2.先将ActionBar设置为NoActionBar 先打开styles.xml 3.使用工具类 package com.afison.newfault.utils;import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.graph…

【Elasticsearch篇】详解使用RestClient操作索引库的相关操作

文章目录 &#x1f354;什么是Elasticsearch&#x1f33a;什么是RestClient&#x1f386;代码操作⭐初始化RestClient⭐使用RestClient操作索引库⭐使用RestClient删除索引库⭐使用RestClient判断索引库是否存在 &#x1f354;什么是Elasticsearch Elasticsearch是一个开源的分…

SpringBoot 统计更多Api接口SQL相关日志信息

统计(查询,更新,批量更新)SQL执行次数及用时并输出log import com.zhangziwa.practisesvr.utils.log.LogContext; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts…

什么是通配监听端口? 什么是通配监听IP?

什么是通配监听端口? 监听端口&#xff1a; 指的是服务器或服务开启的特定TCP或UDP端口号&#xff0c;等待客户端连接或发送数据。TCP/IP协议下每个端口只能由一个服务独占监听&#xff0c;一个服务或应用会指定监听特定的一个或多个端口来接收客户端的连接请求。 例如 Web…

RocketMQ-Windows版本安装

RocketMQ-Windows版本安装 1.环境准备 JDK和maven需要先安装好&#xff0c;我这里使用的JDK1.8版本 Maven 3.8.6版本。需要注意的是&#xff0c;这里配置java时需要指定JAVA_HOME环境变量 RokectMQ才能正常启动。 2.下载RocketMQ 官网下载: https://rocketmq.apache.org/z…

C++读取txt文件中的逐个字符

为了增加读取的灵活性&#xff0c;所以separator和filename都设置为在主函数中获取输入或者在函数中传参的视线方法 举个例子&#xff0c;txt文件如下&#xff1a; household;2;true; 首先声明一个读取数据的文件 void read_data_file(const string& filename,char se…

Matplotlib Mastery: 从基础到高级的数据可视化指南【第30篇—python:数据可视化】

文章目录 Matplotlib: 强大的数据可视化工具1. 基础1.1 安装Matplotlib1.2 创建第一个简单的图表1.3 图表的基本组件&#xff1a;标题、轴标签、图例 2. 常见图表类型2.1 折线图2.2 散点图2.3 条形图2.4 直方图 3. 图表样式与定制3.1 颜色、线型、标记的定制3.2 背景样式与颜色…

CC工具箱使用指南:【属性映射】

一、简介 在规划工作中&#xff0c;经常会遇到这样一种情况&#xff0c;有一组一一对应的值。 比如用地编码和用地名称&#xff0c;用地编码【0101】和用地名称【水田】是对应的。 当你在用地编码字段输入【0101】时&#xff0c;用地名称值就必须为【水田】。 当我们确定用地…

gin路由篇

1. 基本路由 gin 框架中采用的路由库是基于httprouter做的 import ("net/http""github.com/gin-gonic/gin" )func main() {// 1.创建路由r : gin.Default()// 2.绑定路由规则&#xff0c;执行的函数// gin.Context&#xff0c;封装了request和responser.…