Go 基本数据

news2024/11/24 22:52:09

第 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 

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

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

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

类型转换1

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

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

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

示例1

var  apples  int32  =  1

var  oranges  int16  =  2

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

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

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

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

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

类型转换2

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

var x X = x1

var t T = t1

t = T(x)

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

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

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

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

可能会改变值或损失精度

示例2

f  :=  3.141  // a  float64

i  :=  int(f)

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

f = 1.99

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

类型转换3

浮点型转换为整型,会舍弃小数部分,即趋零截尾(正值向下取整,负值向上取整)

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

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

示例3

f  :=  1e100    // a float64

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

整型的 3 种

进制表示

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

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

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

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

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

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

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

示例4

如下面的例子所示,如果使用 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

说明1

注意 fmt 的两个技巧:

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

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

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

      0x 、0X

说明2

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

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

示例5

用 %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 main

import (
    "image"
    "image/color"
    "image/png"
    "math/cmplx"
    "os"
)

func main() {
    const (
        xmin, ymin, xmax, ymax = -2, -2, +2, +2
        width, height          = 1024, 1024
    )

    img := image.NewRGBA(image.Rect(0, 0, width, height))
    for py := 0; py < height; py++ {
        y := float64(py)/height * (ymax-ymin) + ymin
        for px := 0; px < width; px++ {
            x := float64(px)/width * (xmax-xmin) + xmin
            z := complex(x, y)
            // 点 (px, py) 表示复数数值 z
            img.Set(px, py, mandelbrot(z))
        }
    }
    png.Encode(os.Stdout, img)
}

func mandelbrot(z complex128) color.Color {
    const iterations = 200
    const contrast = 15

    var v complex128
    for n := uint8(0); n < iterations; n++ {
        v = v*v + z
        if cmplx.Abs(v) > 2 {
            return color.Gray(255 - contrast*n)
        }
    }
    return color.Black
}

4. 布尔值(OK)

布尔类型

Go 语言中用 bool

声明布尔类型数据

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)
示例 1

s  :=  "hello,world"

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

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

越界访问

触发宕机

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

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

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

提取子串

切片操作

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

子串

默认起始位置

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

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

示例 4

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

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

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

字符串连接加号( + )运算符,连接两个字符串而生成一个新字符串
示例 5fmt.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

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

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

相关文章

VUE---插槽

一、插槽的作用&场景 1、在封装组件的时候&#xff0c;将可变的结构设计为插槽&#xff08;<slot></slot>&#xff09; 2、使用上述组件的时候&#xff0c;可以按需为插槽提供自定义的结构&#xff0c;以达到复用组件且高度自定的效果 二、基本语法 1、组件内…

关于网络安全 的 ARP欺骗 实验操作

实验设备&#xff1a; Windows server 2008 kali 1. vmware--上面菜单栏--虚拟机--设置--网络--NAT 模式 确定靶机与攻击机的连通性&#xff08;互相能 ping 通&#xff09; 靶机查看 arp 表&#xff08;arp -a&#xff09; 查看攻击机(kali)物理地址&#xff08;ip addr&…

MIT 6s081 lab4.xv6进程调度

xv6进程调度 在xv6中&#xff0c;调度发生的两种情况&#xff1a; 时钟中断导致的进程切换&#xff08;也叫时间片轮转&#xff09;睡眠锁&#xff0c;当进程调用sleep时&#xff0c;发生cpu的调度 xv6进程相关概念 xv6用struct proc来描述进程 // Per-process state stru…

uvicorn日志清空问题以及uvicorn日志配置

uvicorn日志清空问题 1、配置&#xff1a; uvicorn starlette 2、现象描述&#xff1a; 当我使用uvicorn starlette进行Python web开发的时候&#xff0c;本来想把所有的日志都打印到一个文件里面&#xff0c;于是我写了一个启动脚本&#xff0c;所有的日志都输出到log.t…

最大流-Dinic算法,原理详解,四大优化,详细代码

文章目录 零、前言一、概念回顾(可略过)1.1流网络1.2流1.3最大流1.4残留网络1.5增广路径1.6流网络的割1.7最大流最小割定理1.7.1证明 1.8Ford-Fulkerson方法 二、Dinic算法2.1EK算法的可优化之处2.2Dinic算法的优化策略2.3Dinic算法原理2.3.1找增广路2.3.2更新剩余容量 2.4算法…

浏览器无网

目录 1.运行网络诊断&#xff0c;确认原因 原因A.远程计算机或设备将不接受连接(该设备或资源(Web 代理)未设置为接受端口“7890”上的连接 原因B.DNS服务器未响应 场景A.其他的浏览器可以打开网页&#xff0c;自带的Edge却不行 方法A&#xff1a;关闭代理 Google自带翻译…

<C++>STL->vector

vector的介绍 vector的使用文档 vector是一个可改变数组大小的序列容器vector和数组一样采取连续的空间存放数据&#xff0c;可以使用方括号访问vector的元素&#xff0c;和数组一样高效。但是vector的大小可以动态增长&#xff0c;而数组不行实际上vector内部使用一个动态分…

MySQL-SQL-DQL

DQL-介绍 DQL-语法 基本查询 1、查询多个字段 2、设置别名 3、去除重复记录 条件查询 1、语法 2、条件 聚合函数 1、介绍 2、常见的聚合函数 3、语法 分组查询 1、语法 2、where与having区别 排序查询 1、语法 2、排序方式 分页查询 1、语法 DQL-执行顺序

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测

多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测 目录 多维时序 | Matlab实现WOA-TCN-Multihead-Attention鲸鱼算法优化时间卷积网络结合多头注意力机制多变量时间序列预测效果一览基本介绍程序设计参考资料 效…

C++大学教程(第九版)6.29素数

题目 (素数)素数是只能被1和自已整除的整数。例如,235和7是素数而468和9不是素数 a)编写一个函数&#xff0c;确定一个数是否是素数。 b)在程序中使用这个函数&#xff0c;该程序确定和打印2 ~10000之间的所有素数。在确信已找到所有的素数之前&#xff0c;实际需测试这些数中…

五邑大学餐厅网络点餐系统设计与实现(包含完整源码详细开发过程)

博主介绍&#xff1a;✌专研于前后端领域优质创作者、本质互联网精神开源贡献答疑解惑、坚持优质作品共享、掘金/腾讯云/阿里云等平台优质作者、擅长前后端项目开发和毕业项目实战&#xff0c;深受全网粉丝喜爱与支持✌有需要可以联系作者我哦&#xff01; &#x1f345;文末获…

你不知道的git如何撤销回退版本

简言之&#xff1a;从1 回退到 3&#xff0c;在3版本通过回退记录(git reflog)找到它的上一条回退记录的hash值&#xff0c;复制1的hash值进行回退&#xff0c;执行git reset --hard 粘贴1的hash值进来&#xff0c;此时就回到1的版本了&#xff0c;执行git log即可看到1、2、3、…

安装pytorch GPU的方法,一次安装成功!!win10、win11皆可用!!

前提—查看是否有NVIDIV英伟达显卡&#xff01;&#xff01; 在控制面板打开设备管理器 一、查看电脑的显卡驱动版本 方法一&#xff1a;在cmd命令窗口中输入nvidia-smi&#xff0c;可以发现版本为12.2 方法2&#xff1a;点击NVIDIA控制面板→系统信息 二、安装CUDA 方法1…

算法(4)——前缀和

目录 一、前缀和的定义 二、一维前缀和 三、一维前缀和OJ题 3.1、前缀和 3.2、寻找数组中心下标 3.3、除自身以外数组的乘积 3.4、和为K的数组 3.5、和可被K整除的子数组 3.6、连续数组 四、二位前缀和 4.1、二维前缀和 4.2、矩阵区域和 一、前缀和的定义 对于一个…

探索全球DNS体系 | 从根服务器到本地解析

DNS 发展 DNS&#xff08;Domain Name System&#xff09;的起源可以追溯到互联网早期。 早期的挑战&#xff1a; 早期互联网主要通过IP地址进行通信&#xff0c;用户需要记住复杂的数字串来访问网站。 需求的催生&#xff1a; 随着互联网的扩大&#xff0c;更简单、易记的…

pytest+allure 生成中文报告

背景 已安装pytestallure&#xff0c;生成的报告是英文 allure生成中文报告 参考&#xff1a;allure report 报告中文化及其它优化 方法1&#xff1a;直接在报告中切换中文 方法2&#xff1a;依赖系统中文语言 创建一个setting.js 文件在index.html 同级目录 // 尝试从 l…

java基于安卓开发的流浪动物救助移动应用的设计与实现-计算机毕业设计源码12783

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;流浪动物救助系统被用户普遍使用&#xff0c;为方便用户能…

前端面试题-(BFC,前端尺寸单位,网站页面常见的优化手段)

前端面试题-BFC&#xff0c;前端尺寸单位&#xff0c;网站页面常见的优化手段 BFC前端尺寸单位网站页面常见的优化手段 BFC BFC&#xff08;block formartting context&#xff09;块格式化上下文。是通过独立渲染的区域&#xff0c;它拥有自己的渲染规则&#xff0c;可以决定…

51单片机LCD1602调试工具

参考视频&#xff1a;江协科技51单片机 LCD1602头文件代码 #ifndef __LCD1602_H__ #define __LCD1602_H__//用户调用函数&#xff1a; void LCD_Init(); void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char); void LCD_ShowString(unsigned char Line,un…

纷享销客盛邀渠道生态伙伴共赴杭州,凝心聚力共谋未来

2024年1月19日&#xff0c;“凝心聚力 勇立潮头——2024纷享销客首场生态伙伴发展共建会”在杭州绿谷举办。此次会议汇聚了各方60余位伙伴到场&#xff0c;共同探讨行业的未来发展趋势&#xff0c;激发创新力和合作潜力。 会上&#xff0c;纷享销客创始人兼CEO罗旭详尽地介绍了…