科普一下开云体育APP官方下载2023(V18.32已更新)

欢迎访问开云体育APP官方下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

编注:

继续阅读

科普一下开云app官方版最新下载(2023v18.6版已更新)

欢迎访问开云app官方版最新下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

本篇文章带大家聊聊go语言中的限流漏桶和令牌桶库,介绍令牌桶和漏桶的实现原理以及在实际项目中简单应用。

为什么需要限流中间件?

在大数据量高并发访问时,经常会出现服务或接口面对大量的请求而导致数据库崩溃的情况,甚至引发连锁反映导致整个系统崩溃。或者有人恶意攻击网站,大量的无用请求出现会导致缓存穿透的情况出现。使用限流中间件可以在短时间内对请求进行限制数量,起到降级的作用,从而保障了网站的安全性。

应对大量并发请求的策略?

  • 使用消息中间件进行统一限制(降速)

  • 使用限流方案将多余请求返回(限流)

  • 升级服务器

  • 缓存(但仍然有缓存穿透等危险)

  • 等等

可以看出在代码已经无法提升的情况下,只能去提升硬件水平。或者改动架构再加一层!也可以使用消息中间件统一处理。而结合看来,限流方案是一种既不需要大幅改动也不需要高额开销的策略。

常见的限流方案

  • 令牌桶算法

  • 漏桶算法

  • 滑动窗口算法

  • 等等

漏桶

引入ratelimit库

go get -u go.uber.org/ratelimit

库函数源代码

 // New returns a Limiter that will limit to the given RPS.
func New(rate int, opts ...Option) Limiter {
return newAtomicBased(rate, opts...)
}
// newAtomicBased returns a new atomic based limiter.
func newAtomicBased(rate int, opts ...Option) *atomicLimiter {
// TODO consider moving config building to the implementation
// independent code.
config := buildConfig(opts)
perRequest := config.per / time.Duration(rate)
l := &atomicLimiter{
perRequest: perRequest,
maxSlack:   -1 * time.Duration(config.slack) * perRequest,
clock:      config.clock,
}
initialState := state{
last:     time.Time{},
sleepFor: 0,
}
atomic.StorePointer(&l.state, unsafe.Pointer(&initialState))
return l
}

该函数使用了函数选项模式多个结构体对象进行初始化

根据传入的值来初始化一个桶结构体 rateint 传参 。

初始化过程中包括了

  • 每一滴水需要的时间 perquest = config.per / time.Duration(rate)
  • maxSlack 宽松度(宽松度为负值)-1 * time.Duration(config.slack) * perRequest 松紧度是用来规范等待时间的
 // Clock is the minimum necessary interface to instantiate a rate limiter with
// a clock or mock clock, compatible with clocks created using
// github.com/andres-erbsen/clock.
type Clock interface {
Now() time.Time
Sleep(time.Duration)
}

同时还需要结构体Clock来记录当前请求的时间now和此刻的请求所需要花费等待的时间sleep

 type state struct {
last     time.Time
sleepFor time.Duration
}

state 主要用来记录上次执行的时间以及当前执行请求需要花费等待的时间(作为中间状态记录)

最重要的Take逻辑

 func (t *atomicLimiter) Take() time.Time {
var (
newState state
taken    bool
interval time.Duration
)
for !taken {
now := t.clock.Now()
previousStatePointer := atomic.LoadPointer(&t.state)
oldState := (*state)(previousStatePointer)
newState = state{
last:     now,
sleepFor: oldState.sleepFor,
}
if oldState.last.IsZero() {
taken = atomic.CompareAndSwapPointer(&t.state, previousStatePointer, unsafe.Pointer(&newState))
continue
}
// 计算是否需要进行等待取水操作
newState.sleepFor += t.perRequest(每两滴水之间的间隔时间) - now.Sub(oldState.last)(当前时间与上次取水时间的间隔)
// 如果等待取水时间特别小,就需要松紧度进行维护
if newState.sleepFor < t.maxSlack {
newState.sleepFor = t.maxSlack
}
// 如果等待时间大于0,就进行更新
if newState.sleepFor > 0 {
newState.last = newState.last.Add(newState.sleepFor)
interval, newState.sleepFor = newState.sleepFor, 0
}
taken = atomic.CompareAndSwapPointer(&t.state, previousStatePointer, unsafe.Pointer(&newState))
}
t.clock.Sleep(interval)
// 最后返回需要等待的时间
return newState.last
}

实现一个Take方法

  • 该Take方法会进行原子性操作(可以理解为加锁和解锁),在大量并发请求下仍可以保证正常使用。

  • 记录下当前的时间 now := t.clock.Now()

  • oldState.last.IsZero()判断是不是第一次取水,如果是就直接将state结构体中的值进行返回。而这个结构体中初始化了上次执行时间,如果是第一次取水就作为当前时间直接传参。

  • 如果 newState.sleepFor 非常小,就会出现问题,因此需要借助宽松度,一旦这个最小值比宽松度小,就用宽松度对取水时间进行维护。

  • 如果newState.sleepFor > 0 就直接更新结构体中上次执行时间newState.last = newState.last.Add(newState.sleepFor)并记录需要等待的时间interval, newState.sleepFor = newState.sleepFor, 0

  • 如果允许取水和等待操作,那就说明没有发生并发竞争的情况,就模拟睡眠时间t.clock.Sleep(interval)。然后将取水的目标时间进行返回,由服务端代码来判断是否打回响应或者等待该时间后继续响应。

t.clock.Sleep(interval)

 func (c *clock) Sleep(d time.Duration) { time.Sleep(d) }

实际上在一个请求来的时候,限流器就会进行睡眠对应的时间,并在睡眠后将最新取水时间返回。

实际应用(使用Gin框架)

 func ratelimit1() func(ctx *gin.Context) {
r1 := rate1.New(100)
return func(ctx *gin.Context) {
now := time.Now()
//  Take 返回的是一个 time.Duration的时间
if r1.Take().Sub(now) > 0 {
// 返回的时间比当前的时间还大,说明需要进行等待
// 如果需要等待, 就 time.Sleep(r1.Take().Sub(now())) 然后放行
// 如果不需要等待请求时间,就直接进行Abort 然后返回
response(ctx, http.StatusRequestTimeout, "rate1 limit...")
fmt.Println("rate1 limit...")
ctx.Abort()
return
}
// 放行
ctx.Next()
}
}

这里你可以进行选择是否返回。因为Take一定会执行sleep函数,所以当执行take结束后表示当前请求已经接到了水。当前演示使用第一种情况。

  • 如果你的业务要求响应不允许进行等待。那么可以在该请求接完水之后然后,如上例。

  • 如果你的业务允许响应等待,那么该请求等待对应的接水时间后进行下一步。具体代码就是将if中的内容直接忽略。(建议使用)

测试代码

这里定义了一个响应函数和一个handler函数方便测试

 func response(c *gin.Context, code int, info any) {
c.JSON(code, info)
}
func pingHandler(c *gin.Context) {
response(c, 200, "ping ok~")
}

执行go test -run=Run -v先开启一个web服务

 func TestRun(t *testing.T) {
r := gin.Default()
r.GET("/ping1", ratelimit1(), pingHandler)
r.GET("/ping2", ratelimit2(), helloHandler)
_ = r.Run(":4399")
}

使用接口压力测试工具go-wrk进行测试->tsliwowicz/go-wrk: go-wrk)

在golang引入install版本可以直接通过go install github.com/tsliwowicz/go-wrk@latest下载

使用帮助

    Usage: go-wrk <options> <url>
Options:
-H       Header to add to each request (you can define multiple -H flags) (Default )
-M       HTTP method (Default GET)
-T       Socket/request timeout in ms (Default 1000)
-body    request body string or @filename (Default )
-c       Number of goroutines to use (concurrent connections) (Default 10)
-ca      CA file to verify peer against (SSL/TLS) (Default )
-cert    CA certificate file to verify peer against (SSL/TLS) (Default )
-d       Duration of test in seconds (Default 10)
-f       Playback file name (Default <empty>)
-help    Print help (Default false)
-host    Host Header (Default )
-http    Use HTTP/2 (Default true)
-key     Private key file name (SSL/TLS (Default )
-no-c    Disable Compression - Prevents sending the "Accept-Encoding: gzip" header (Default false)
-no-ka   Disable KeepAlive - prevents re-use of TCP connections between different HTTP requests (Default false)
-no-vr   Skip verifying SSL certificate of the server (Default false)
-redir   Allow Redirects (Default false)
-v       Print version details (Default false)

-t 8个线程 -c 400个连接 -n 模拟100次请求 -d 替换-n 表示连接时间

输入go-wrk -t=8 -c=400 -n=100 http://127.0.0.1:4399/ping1

可以稍微等待一下水流积攒(压测速度过快)。

image.png
可以看出,89个请求全部返回。也就是说在一段请求高峰期,不会有请求进行响应。因此我认为既然内部已经睡眠,那么就也就应该对请求放行处理。

令牌桶

引入ratelimit

go get -u github.com/juju/ratelimit

初始化

 // NewBucket returns a new token bucket that fills at the
// rate of one token every fillInterval, up to the given
// maximum capacity. Both arguments must be
// positive. The bucket is initially full.
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket {
return NewBucketWithClock(fillInterval, capacity, nil)
}
// NewBucketWithClock is identical to NewBucket but injects a testable clock
// interface.
func NewBucketWithClock(fillInterval time.Duration, capacity int64, clock Clock) *Bucket {
return NewBucketWithQuantumAndClock(fillInterval, capacity, 1, clock)
}

进行Bucket桶的初始化。

 func NewBucketWithQuantumAndClock(fillInterval time.Duration, capacity, quantum int64, clock Clock) *Bucket {
if clock == nil {
clock = realClock{}
}
// 填充速率
if fillInterval <= 0 {
panic("token bucket fill interval is not > 0")
}
// 最大令牌容量
if capacity <= 0 {
panic("token bucket capacity is not > 0")
}
// 单次令牌生成量
if quantum <= 0 {
panic("token bucket quantum is not > 0")
}
return &Bucket{
clock:           clock,
startTime:       clock.Now(),
latestTick:      0,
fillInterval:    fillInterval,
capacity:        capacity,
quantum:         quantum,
availableTokens: capacity,
}
}

令牌桶初始化过程,初始化结构体 fillInterval(填充速率) cap(最大令牌量) quannum(每次令牌生成量)。

如果三个变量有一个小于或者等于0的话直接进行报错返回。在最开始就将当前令牌数初始化为最大容量

调用

 // TakeAvailable takes up to count immediately available tokens from the
// bucket. It returns the number of tokens removed, or zero if there are
// no available tokens. It does not block.
func (tb *Bucket) TakeAvailable(count int64) int64 {
tb.mu.Lock()
defer tb.mu.Unlock()
return tb.takeAvailable(tb.clock.Now(), count)
}

调用TakeAvailable函数,传入参数为需要取出的令牌数量,返回参数是实际能够取出的令牌数量。

内部实现

 func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 {
// 如果需要取出的令牌数小于等于零,那么就返回0个令牌
if count <= 0 {
return 0
}
// 根据时间对当前桶中令牌数进行计算
tb.adjustavailableTokens(tb.currentTick(now))
// 计算之后的令牌总数小于等于0,说明当前令牌不足取出,那么就直接返回0个令牌
if tb.availableTokens <= 0 {
return 0
}
// 如果当前存储的令牌数量多于请求数量,那么就返回取出令牌数
if count > tb.availableTokens {
count = tb.availableTokens
}
// 调整令牌数
tb.availableTokens -= count
return count
}
  • 如果需要取出的令牌数小于等于零,那么就返回0个令牌

  • 根据时间对当前桶中令牌数进行计算

  • 计算之后的令牌总数小于等于0,说明当前令牌不足取出,那么就直接返回0个令牌

  • 如果当前存储的令牌数量多于请求数量,那么就返回取出令牌数

  • 调整令牌数

调整令牌

 func (tb *Bucket) adjustavailableTokens(tick int64) {
lastTick := tb.latestTick
tb.latestTick = tick
// 如果当前令牌数大于最大等于容量,直接返回最大容量
if tb.availableTokens >= tb.capacity {
return
}
// 当前令牌数 += (当前时间 - 上次取出令牌数的时间) * quannum(每次生成令牌量)
tb.availableTokens += (tick - lastTick) * tb.quantum
// 如果当前令牌数大于最大等于容量, 将当前令牌数 = 最大容量 然后返回 当前令牌数
if tb.availableTokens > tb.capacity {
tb.availableTokens = tb.capacity
}
return
}
  • 如果当前令牌数大于最大等于容量,直接返回最大容量

  • 当前令牌数 += (当前时间 – 上次取出令牌数的时间) * quannum(每次生成令牌量)

  • 如果当前令牌数大于最大等于容量, 将当前令牌数 = 最大容量 然后返回 当前令牌数

实现原理

  • 加锁 defer 解锁

  • 判断count(想要取出的令牌数) 是否小于等于 0,如果是直接返回 0

  • 调用函数adjustTokens 获取可用的令牌数量

  • 如果当前可以取出的令牌数小于等于0 直接返回 0

  • 如果当前可以取出的令牌数小于当前想要取出的令牌数(count) count = 当前可以取出的令牌数

  • 当前的令牌数 -= 取出的令牌数 (count)

  • 返回 count(可以取出的令牌数)

额外介绍

take函数,能够返回等待时间和布尔值,允许欠账,没有令牌也可以取出。

func (tb *Bucket) Take(count int64) time.Duration

takeMaxDuration函数,可以根据最大等待时间来进行判断。

func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)

因为他们内部的实现都基于令牌调整,我这里不做过多介绍,如果感兴趣可以自行研究一下。

测试

 func ratelimit2() func(ctx *gin.Context) {
// 生成速率 最大容量
r2 := rate2.NewBucket(time.Second, 200)
return func(ctx *gin.Context) {
//r2.Take() // 允许欠账,令牌不够也可以接收请求
if r2.TakeAvailable(1) == 1 {
// 如果想要取出1个令牌并且能够取出,就放行
ctx.Next()
return
}
response(ctx, http.StatusRequestTimeout, "rate2 limit...")
ctx.Abort()
return
}
}

image.png
压测速度过于快速,在实际过程中可以根据调整令牌生成速率来进行具体限流!

小结

令牌桶可以允许自己判断请求是否继续,内部不会进行睡眠操作。而漏桶需要进行睡眠,并没有提供方法让程序员进行判断是否放行。

【相关推荐:Go视频教程、编程教学】

科普一下开云app官方版最新下载(2023v18.6版已更新)

欢迎访问开云app官方版最新下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

React为什么不将Vite作为构建应用的首选?下面本篇文章就来带大家聊聊React不将Vite作为默认推荐的原因,希望对大家有所帮助!

React文档中,对于构建新的React应用,首推的方式是CRA(create-react-app)。

CRA推出于2016年,彼时还没有成体系的React脚手架工具供大家使用,再加上这是官方工具,一经推出就受到了欢迎。截止当前,CRA仓库已经收获快10wstar

但是,随着时间的推移,出现了很多优秀的替代品,比如parcelvite提供的React模版。

CRA本身的进步速度却在放缓,其上一次提交要追溯到去年9月8日:

此外,CRA对一些流行工具的支持也不是很好,比如在TailwindCSS文档中就不推荐使用CRA

近日,油管10w粉丝的前端网红Theo就在React文档仓库发起了一个PR,号召React文档不要再默认推荐CRA,而是应该将Vite作为构建应用的首选。【相关推荐:Redis视频教程、编程视频】

看这围观群众的数量就知道大家对这种敏感问题有多关心了:

那么,React团队是如何看待这个问题的呢?他们会将Vite作为构建应用的首选项么?

本文来聊聊DanReact核心成员)对这一问题的看法。

欢迎加入人类高质量前端交流群,带飞

CRA的定位

既然众矢之的是CRA,那么首先我们需要明白CRAReact体系下的定位,再来看看Vite能否在这个定位下取代前者。

CRA诞生的时期(2016年),是SPA(单页应用)最火热的时期。在当时,他很好的解决了两个痛点:

0配置初始化项目

这点不用过多介绍,执行如下命令后就能生成一个CSR(客户端渲染)的React项目:

npx create-react-app 项目名复制代码

集成工具链

CRA将当时的一些工程化最佳实践都封装在react-scripts包下,并抹平这些工具不兼容的地方。

开发者既享受了开箱即用的最佳实践,又不用担心某些工具升级后对项目造成的影响(CRA会处理)。

后来的很多优秀脚手架工具(比如ViteParcel),也都沿用了这种开箱即用的理念。

除了以上两点,随着CRA的走红,React团队还将他作为新特性的快速分发渠道,比如:

  • Fast Refresh(针对React的热更新,不会丢失组件状态)

  • Hooks推出后的一系列lint规则

依托CRA庞大的装机量与使用量,这些集成到CRA的特性可以快速部署到开发者的项目中,达到快速提高普及率的目的。

试想,如果没有CRA的推动,Hookslint规则很难在开发者中有这么高普及率,Hooks的理念也就不会这么快席卷整个前端框架领域。

从以上三点来看,Vite完全可以成为比CRA性能更优的替代品。

但是,React团队的考量不仅如此。

脚手架工具的不足

虽然CRA开箱即用,但他提供的能力并不全面,比如他并不提供:

  • 状态管理方案

  • 路由方案

  • 数据请求方案

为什么不提供呢?因为在CRA发展的时期,这些方案还未形成最佳实践。

随着时间发展,开发者逐渐摸索出解决这些问题的最佳实践。比如请求瀑布问题,考虑如下组件:

function App() {  const [data, update] = useState(null);  useEffect(() => {    fetch('http://...').then(res => update(res.json()))
}, [])
return <Child data={data}/>}复制代码

只有当App组件渲染后才能开始请求数据,这个请求时机是比较滞后的,如果Child依赖data来请求自己的数据,那么由于App请求的滞后导致Child的请求也滞后了,这就是请求瀑布问题。

这个问题常见的解决方法是 —— 将请求数据的逻辑收敛到路由方案中。

再比如,随着业务不断迭代,业务代码体积越来越大,常见的优化手段是懒加载组件。

但是,手动执行懒加载常常会产生意料之外的问题。比如,页面中有个图表组件<Chart/>,如果开发者懒加载了这个组件,但是该组件在on mount时请求数据,这又会陷入请求瀑布问题。

要彻底解决这个问题,需要配合3类技术方案:

  • 数据请求方案(解决数据流向问题)

  • 路由方案(解决数据请求时机问题)

  • 打包方案(解决懒加载的实现问题)

类似的问题还有很多,比如CSR首屏渲染速度慢的问题(需要通过SSR解决)。

可见,CRA仅仅提供了CSR环境下一个开箱即用的模版,但是随着项目变得越来越复杂,一些业务细节问题CRA是没有提供开箱即用的解决方案的。

从这个角度看,即使切换到Vite还是会面临同样的问题。

新时代的框架

随着各种常见问题的最佳实践被探索出来,逐渐诞生了一些以React为基础,集成各种业务问题最佳实践的框架,比如Next.jsRemix

其中,Remix就是以React-Router(路由解决方案)为基础,逐渐发展出来的囊括路由、数据请求、渲染为一体的全栈框架。

那么,能否将CRA迭代为类似Next.jsRemix这样的全栈框架,一劳永逸解决CRA对各种最佳实践的缺失呢?

React团队认为,这样做需要极高的开发成本,而且随着时代发展,总会出现更多CRA不支持的最佳实践(就像他当前面临的问题一样),那么CRA终有一天会被再度淘汰。

所以,这个方案不可取。

既然这个方案不可取,那么用Vite取代CRA的方案也不可取。因为单纯使用Vite并没有解决最佳实践的缺失,必须在此基础上实现那些最佳实践(比如路由、数据请求…),那又回到了开发一个全栈框架

最终,React团队更倾向如下解决方案:将CRA作为一个脚手架工具,启动后会根据用户的不同场景需要(比如是SSR还是CSR)推荐不同的框架,再将CRA作为不使用框架情况下的兜底方案

并且,在实现上,可能将兜底方案中的webpack切换为Vite

总结

React团队的思考可以发现,React始终将自己定位为一个状态驱动UI的库。

随着时代的发展,单独使用这个库已经不能满足日常开发需要,基于底层使用React + 实现各种最佳实践模式的框架会越来越流行。

最近,Next.js达到了10wstar成就,成为Githubstar排名第14的仓库,间接印证了这种趋势。

回到开篇的问题:React为什么不将Vite作为默认推荐?

如果是用Vite取代webpack作为CRA的打包工具,未来可能会。但是,这不是最首要的问题。

如何协助上层的框架更好的服务开发者,才是React团队首要考虑的问题。

React不死,他只会逐渐移居幕后。

【推荐学习:javascript视频教程】

科普一下开云app官方版最新下载(2023v18.6版已更新)

欢迎访问开云app官方版最新下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

php实现字符串去掉头尾的方法:1、使用ltrim函数删除字符串开头的空白字符或其他字符;2、使用rtrim函数删除字符串末端的空白字符或者其他字符即可。

本教程操作环境:Windows10系统、PHP8.1版、DELL G3电脑

php 怎么实现字符串去掉头尾?

PHP字符串去除首尾指定字符的trim ltrim rtrim函数

今天,我们看一个比较简单的处理字符串函数,我们都知道在表单提交的过程中,可用户输入的内容不一定就是和你想的一样合法的数据,就比如输入用户名或者邮箱的时候在input框空了几个空格,这样进入数据库中的数据自然也会多出几个空格显然不太符合逻辑,所以一般我们可以用trim来去除前后空格的效果。

trim函数

语法:trim ( string $str [, string $character_mask ] ) : string

作用:删除字符串首尾处的空白字符(或者其他字符)

返回值:返回处理后的字符串

同系列的函数有ltrim rtrim函数

ltrim — 删除字符串开头的空白字符(或其他字符)

rtrim — 删除字符串末端的空白字符(或者其他字符)

一个参数

函数里面只使用一个参数只能去除指定字符。不使用第二个参数,trim() 仅删除以下字符:

d61fa7a18914832d1aa985b52c8662d.jpg

两个参数

通过指定 character_mask,可以指定想要删除的字符列表。简单地列出你想要删除的全部字符。或者通过使用 .. 格式,可以指定一个范围。

#trim去除前后指定字符列表
$str = "Hello Tacks";
var_dump($str);
var_dump(trim($str,'Hes'));
#删除 $binary 开头的 ASCII 控制字符
$binary = "\x09Example string\x0A";
// (从 0 到 31,包括 0 和 31)
$clean = trim($binary, "\x00..\x1F");
var_dump($clean);

emmm,其他两个函数ltrim和rtrim函数用法一致,这里就不再累述。

小例子

你以为就要结束了吗,你以为这个函数很简单。那你看一下下面的代码。

$str = "Hello php";
var_dump($str);
var_dump(rtrim($str,'hp'));

如果你觉得处理后的字符串是'Hello p',那么你就还是没真正意义上理解这个函数第二个参数的用法。

首先我用的是rtrim函数,去除字符串尾部的特定字符,第二这个特定字符是也就是第二个参数,是一个字符列表,那么就是字符串从右向左不断匹配字符列表里面的字符,所以会匹配p和h,因此最终的结果是'Hello '。所以还是不要大意而用错函数达不到你想要的结果。

推荐学习:《PHP视频教程》

科普一下开云app官方版最新下载(2023v18.6版已更新)

欢迎访问开云app官方版最新下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

php判断字符串是否有中文的方法:1、新建一个php文件;2、使用header()方法设置页面的编码格式为utf-8;3、将一个含有中文的字符串存放在$str变量中;4、使用preg_match函数利用正则匹配中文;5、通过if语句对上一步的结果进行判断,然后通过echo输出不同的提示结果即可。

本教程操作环境:Windows10系统、PHP8.1版、DELL G3电脑

php 怎么判断字符串是否有中文?

新建一个php文件,命名为test.php,用于讲解PHP中如何判断字符串是否含有中文。愁栗争

fbd99fcd9a45e31e343fd8171949e4d.jpg

在test.php文件中,使用header()方法设置页面的编码格式为utf-8,避免页面输出中文时乱码。

bb3613985f09e78a7ec54fdd5b6881a.jpg

在test.php文件中,将一个含有中文的字符串存放在$str变量中。

9de6eb2767aec906839a546fae20153.jpg

在test.php文件中,使用preg_match()函数利用正则匹配中文,结果保存在$res变量中。如果字符串中没有中文,结果为false。

6c8ae115b64285e9a58b14acb426f48.jpg

在test.php文件中,通过if语句对上一步的结果进行判断,并通过echo输出不同的提示结果。

eab89cc201519e8f660c447ae2a43cc.jpg

在浏览器打开test.php文件,查看结果。

649a0a1b0f4124ba036bf175ded00c3.jpg

推荐学习:《PHP视频教程》

科普一下开云app官方版最新下载(2023v18.6版已更新)

欢迎访问开云app官方版最新下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

Linux PATH环境变量是决定Shell将到哪些目录中寻找命令或程序的一个参数,它的内容是由一堆目录组成的,各目录之间用冒号 “:” 隔开。当执行某个Linux命令时,Linux会依照PATH环境变量中包含的目录依次搜寻该命令的可执行文件,一旦找到,即正常执行;反之,则提示无法找到该命令。

本教程操作环境:linux7.3系统、Dell G3电脑。

什么是环境变量

Linux是一个多用户操作系统,每个用户都有自己专有的运行环境。用户所使用的环境由一系列变量所定义,这些变量被称为环境变量。系统环境变量通常都是大写的。

每个用户都可以根据需要修改自己的环境变量,以达到自己的使用要求。常见的环境变量如下表:

序号 变量 说明
1 PATH 决定了Shell将到哪些目录中寻找命令程序,这个变量是在日常使用中经常需要修改的变量
2 TERM 指定系统终端
3 SHELL 当前用户Shell类型
4 HOME 当前用户主目录
5 LOGNAME 当前用户的登录名
6 USER 当前用户名
7 HISTSIZE 历史命令记录数
8 HOSTNAME 主机的名称
9 LANGUAGE 语言相关的环境变量多语言可以修改此环境变量
10 MAIL 当前用户的邮件存放目录
11 PS1 基本提示符 :
(1)root用户是#
(2)普通用户是$
12 PS2 附属提示符,默认是 >
13 LS_COLORS ls命令结果颜色显示

Linux PATH环境变量

Linux 中的 PATH 环境变量 的内容是由一堆目录组成的,各目录之间用冒号 “:” 隔开。当执行某个 Linux 命令时,Linux 会依照 PATH 环境变量中包含的目录依次搜寻该命令的可执行文件,一旦找到,即正常执行;反之,则提示无法找到该命令。

说明

也就是说当用户在 shell 命令行界面中输入一个 外部命令 时, shell 必须搜索系统来找到对应的程序。 PATH 环境变量定义了用于进行命令和程序查找的目录。

如果命令或者程序的位置没有包括在 PATH 变量中,那么如果不使用绝对路径的话, shell 是没法找到的。如果想要在虚拟目录结构中的任何位置执行某个程序,办法是把这个程序所在的目录添加到 PATH 环境变量中,或者把这个程序放在 / 链接(ln) 到已经存在 PATH 中的目录下。

查看PATH环境变量

命令行中输入

echo $PATH

或者输入:

export

例1:查看环境变量 echo $PATH

1.png

例2:查看环境变量 export

2.png

相关推荐:《Linux视频教程》

科普一下开云app官方版最新下载(2023v18.6版已更新)

欢迎访问开云app官方版最新下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

区别:1、动态库的后缀为“.so”,静态库的后缀为“.a”。2、如果静态函数库改变了,那么程序必须重新编译;而动态函数库的改变并不影响程序。3、相对于静态库,动态库在编译的时候并没有被编译进目标代码中,用户的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。

本教程操作环境:linux7.3系统、Dell G3电脑。

一、库的基础概念:

在windows平台和linux平台下都大量存在着库。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。由于windows和linux的本质不同,因此二者库的二进制是不兼容的。通俗的说就是把这些常用函数的目标文件打包在一起,提供相应函数的接口,便于程序员使用。在使用函数时,只需要包对应的头文件即可。按照库的使用方式又可分为动态库和静态库,在不同平台下对应后缀也有所不同。

WINDOWS下:.dll 后缀为动态库,.lib 后缀为静态库;

LINUX下:.so后缀为动态库,.a后缀为静态库。

二、静态库与静态链接

<1>静态库:

静态库可以简单的看成一组目标文件的集合,即很多目标文件经过压缩打包后形成的文件。比如在我们日常编程中,如果需要使用printf函数,就需要包stdio.h的库文件,使用strlen时,又需要包string.h的库文件,可是如果直接把对应函数源码编译后形成的.o文件直接提供给我们,将会对我们的管理和使用上造成极大不便,于是可以使用“ar”压缩程序将这些目标文件压缩在一起,形成libx.a静态库文件。

注:静态库命名格式:lib + "库名称”+ .a(后缀) 例:libadd.a就是一个叫add的静态库

<2>静态链接:

对于静态库,程序在编译链接时,将库的代码链接到可执行文件中,程序运行时不再需要静态库。在使用过程中只需要将库和我们的程序编译后的文件链接在一起就可形成一个可执行文件。

通过一个例子来了解下如何将我们自己写的头文件和代码同时进行编译链接,最终生成可执行文件:

/main.c/
#include <stdio.h>
#include "add.h"
int main()
{
int ret = add(3, 4);
printf("3 + 4 = %d\n",ret);
return 0;
}
/add.c/
#include "add.h"
int add( int x, int y)
{
return x + y;
}
/add.h/
#pragma once
#include <stdio.h>
int add( int x, int y);
/Makefile/
main : main.c libadd.a
gcc main.c -L . -ladd -o main
//-L为指定路径 .为当前目录下 -l+库名字,编译器可在指定目录下自己寻找名为add的库文件
libadd.a :
gcc -c add.c -o add.o
//ar -rc将多个编译后的文件打包为一个静态库文件
ar -rc libadd.a add.o
.PHONY:clean
clean:
rm main libadd.a

make后输出截图:

这里写图片描述

<3>缺点:

1、内存和磁盘空间浪费:静态链接方式对于计算机内存和磁盘的空间浪费十分严重。

假如一个c语言的静态库大小为1MB,系统中有100个需要使用到该库文件,采用静态链接的话,就要浪费进100M的内存,若数量再大,那浪费的也就更多。例如下图:程序1和程序2都需要用到Lib.o,采用静态链接的话,那么物理内存中就会存放两份对应此文件的拷贝。

这里写图片描述

2、更新麻烦:

比如一个程序20个模块,每个模块只有1MB,那么每次更新任何一个模块,用户都得重新下载20M的程序。

三、动态库与动态链接

<1>动态库:

程序在运行时才去链接动态库的代码,多个程序共享库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。

注:动态库命名格式:lib + "库名称”+ .so(后缀) 例:libadd.so就是一个叫add的动态库

<2>动态链接:

由于静态链接具有浪费内存和模块更新困难等问题,提出了动态链接。基本实现思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将他们链接在一起形成一个完整的程序,而不是像静态链接那样把所有的程序模块都链接成一个单独的可执行文件。所以动态链接是将链接过程推迟到了运行时才进行。

同样,假如有程序1,程序2,和Lib.o三个文件,程序1和程序2在执行时都需要用到Lib.o文件,当运行程序1时,系统首先加载程序1,当发现需要Lib.o文件时,也同样加载到内存,再去加载程序2当发现也同样需要用到Lib.o文件时,则不需要重新加载Lib.o,只需要将程序2和Lib.o文件链接起来即可,内存中始终只存在一份Lib.o文件。

这里写图片描述

动态库和动态链接的例子依然使用上面的代码,输出结果也相同,唯一需要改变的就是Makefile文件。

/Makefile/
main : main.c libadd.so
gcc main.c -L . -ladd -o main
libadd.so :
gcc -fPIC -shared add.c -o libadd.so
//-shared表示输出结果是共享库类型的  -fPIC表示使用地址无关代码奇数来生产输出文件
.PHONY:clean
clean:
rm main libadd.so
  • 当我们生成可执行文件后,可使用ldd命令查看该可执行文件所依靠的动态库。

这里写图片描述

  • 前面提到windows和Linux下库文件的后缀不同,更根本的原因在于二者文件格式都不同。可以通过file一个动态库查看Linux下动态库的文件类型其实是ELF格式。ELF动态链接文件被称为动态共享对象(DSO,Dynamic Shared Objects),简称共享对象;在windows下,动态链接文件被称为动态链接库(Dynamic Linking Library),也就是.dll文件后缀的全称。

<3>优点

  • ①毋庸置疑的就是节省内存;

  • ②减少物理页面的换入换出;

  • ③在升级某个模块时,理论上只需要将对应旧的目标文件覆盖掉即可。新版本的目标文件会被自动装载到内存中并且链接起来;

  • ④程序在运行时可以动态的选择加载各种程序模块,实现程序的扩展。

四、静态库和动态库的区别

1. 静态库

这类库的名字一般是 libxxx.a ;利用静态函数库编译成的文件比较大,因为整个 函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为 如果静态函数库改变了,那么你的程序必须重新编译 。

2. 动态库

这类库的名字一般是 libxxx.so ;相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。 动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。

相关推荐:《Linux视频教程》

科普一下开云体育APP官方下载2023(V18.32已更新)

欢迎访问开云体育APP官方下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

美国作家 H. Jackson Brown Jr. 说过一句很有名的话:

继续阅读

科普一下开云体育APP官方下载2023(V18.32已更新)

欢迎访问开云体育APP官方下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

你可能错过的新鲜和不新鲜事

继续阅读

开云·体育app(中国)官方下载_最新手机版app下载

欢迎访问开云体育app官方下载(KAIYUN)旗下有开云体育手机app下载,开云体育app下载,开云体育官网入口,开云体育app下载官网,开云体育app官网入口手机版等业务,开云体育平台app安全靠谱信誉高!开云体育官网入口

大结局了!《狂飙》——姐姐范穿搭教科书:孟钰、陈书婷、程程、高启兰,你更 pick 谁?

前几天,全网沸腾了一个月的开年爆剧《狂飙》终于收官。以一个反腐剧该有正确的姿势——好人(安欣)活着,坏人(高启强)入狱收场。
但你们是不是也跟我一样意犹未尽呢?

虽然后期剧情上有些许遗憾,但这部剧还是堪称制作精良,人物丰满的塑造和演员深厚的表演功底,让人一秒入戏,F 在吃到安利后,也没忍住熬夜追剧 …..

在以男性为主的脚本里,女性角色戏份不多,但每一个都性格迥异且鲜明,充满了人格魅力,可以说是集齐了当下大爱的各款 ” 姐姐 “~(这个编剧是懂群众的)

作为穿搭号主,一开始 F 被扒出的一水儿爱马仕吸引了注意力,以为又会是另一部《爱的二八定律》。
但看完整部剧后发现,名包只是其中的点缀,更多是从衣着上不显山露水地展现着主人身份的不一般,能感受到剧组的用心,所以 F 准备以此为蓝本,分享几种姐姐类型的穿搭技巧,希望给追完剧的各位一些启发~
第一类姐姐:野性御姐范

代表人物:陈书婷、程程

剧中陈书婷和程程都是气场十足的御姐型,要说两人之间的区别,则是陈书婷多了些风情万种的女人味,程程偏向又酷又飒的职场女强人。

你们看同样大面积穿红色,气质都不一样。

其中,高叶扮演的陈书婷凭借着角色的人格魅力更是掀起全网 ” 大嫂热 “,从妆容、发型到穿搭都被网友们翻来覆去讨论了一遍又一遍。

而据说整部剧的妆容都是高叶自己完成的,有时还会结合角色去改良化妆师原本的妆容!” 大嫂 ” 有点东西在身上!

于是,很多网友在线求教程,高叶也非常 nice,亲自上手教授。

来看看她这张惊艳无数网友的经典造型,
这是她剧里的第一次出场,背光站在楼梯上,神色沉着魅惑,微微上翘的红唇危险而迷人 ……
陈书婷在外的穿搭多以明亮艳丽为主,有强烈的色彩对比,能一下子给人视觉冲击,即使是耳饰,也都偏向大件突出的,毕竟是 ” 大哥的女人 “,气场不能输,有女人味的同时也透着一股强势的狠劲。
黑色风衣 + 正红色丝绸上衣 + 金属复古耳饰,很好地展现出外刚内柔的特质。
纯白色大衣 + 印花连衣裙 + 白色腋下包,大面积印花图案透着成熟女性的柔美,宽敞素色的大衣则平衡了内里的繁杂感,同时,领口是采用的领边线缝设计,细节处彰显了品位,整体大方有气质。
豹纹大衣野性又华贵,红裙成为一套深色的亮点,配黑色尖头细高跟鞋,冷艳霸气攻击性拉满。

戏里的陈书婷常常让 F 想到《危险关系》里的陈数,也是靠着一头精致微卷的短发和复古耳钉,辅以烈焰红唇来凸显强势干练的女人味。

到了程程这里,扮演者曲栅栅之前微博也曾说过,这个角色对于自己是有难度的,但最后还是没有辜负导演的期望,演活了一个职场心狠手辣的女强人形象。
我们知道她在剧中是一个出身底层,靠做题技术考入名牌大学,随后进入到泰叔的建工集团,一步步成长为对高启强最有威胁的竞争对手,这种实打实靠自己杀出一条血路的女强人,造型也是压迫感十足,气场不输 ” 大嫂 “,但比大嫂少了一点女人味,多了一分酷飒和干练。
在造型上,程程一个很大的不同就是留着一头利落且整齐的挂耳短发,脸上没有多余的碎发,配色上也偏向暗色,主要以黑色为主。
卡其色拼接大衣,内里纯黑搭配,中性干练又酷感十足。
又是一套纯黑的皮衣 + 皮裙,金属耳钉和腰间配饰化龙点睛。

偶尔一套简约的羊绒大衣加珍珠耳饰,刻入骨子的刚强里流露出一分柔情。

可以看出,霸气御姐的穿搭重点并不在于爱马仕,而是要会穿!

给大家提供三种增加气场的单品

PART

大衣

大衣款式很多,秋冬必备单品,有质感版型好的大衣自带气场。

H 型的大衣 + 简单 T 恤 + 牛仔裤,披一件毛衣外搭,时尚与保暖兼具。
几个极简品牌已经把近色系或同色系穿搭玩得炉火纯青,廓形呢大衣 + 同色系上衣 + 近色系长裤,哪怕穿双凉鞋混搭都拿捏了气势。
剪裁硬挺的大衣,内搭成套西装或羊毛半裙,职场通勤加分项,不想感觉太正式的可以换成球鞋。PART

PART

夸张耳饰

选购的要点是显眼,御姐要的就不是小家碧玉感,剧里的陈书婷已经很好地给我们来了一波示范。(大一点的耳饰还能显脸小!)

就连 IU 这种甜美系爱豆带大号的耳饰,都一秒御姐起来的!
软萌系小赵也能一秒变许姐。

PART

皮衣

皮衣,永不过时的时髦单品。

皮衣自带霸总属性,分分钟提升气场。

可以无违和地搭上各种单品,柔美型女生穿上后制造出的反差更让人心动。

韩国博主 fever 的几套造型都很适合普通人借鉴,搭配的单品选择复古感的,时髦度就有了。
All Black 造型可以通过显眼的项链或腰带配饰来跳脱沉闷。

第二类姐姐:轻熟文艺风

代表人物:孟钰

作为京海市公安局局长的女儿,也是安欣青梅竹马的 ” 妹妹 “,自幼生活环境的富足,令她天生攻击性少,单纯中带着些许的傲气,穿衣风格偏 ” 大家闺秀 “,前期有少女的浪漫,即使后来人到中年,所嫁非爱和后期丈夫被捕入狱,她的穿衣风格也依旧端庄大气。

这一套是简单实用又容易出气质的穿搭,举手投足间透着一种 ” 白月光 ” 的清新美好,非常适合上班族。
全身颜色很好的控制在三种,其中内衬和外衣以及牛仔裤都是深浅不同的蓝色,甜美又有活力,这里也体现了孟钰当下的感情状态,跟安欣恋爱时甜蜜快乐的心情 ~

此类风格推荐单品

PART

纯色连衣裙

少女感连衣裙,省时省力不费脑,适合度假。
高圆圆在连衣裙穿搭上从来没输过,这一条娃娃款被她穿出了清冷感。

PART

阔腿裤

参考之前孟钰约会时穿的那身,阔腿裤的裤型对梨形身材太友好了,姐姐必备单品 ~

图源见水印

第三类姐姐:冷酷高知感

代表人物:高启兰

高启兰虽然出身贫寒,但靠着 2 个哥哥的庇护,没怎么受过苦,长大后顺利当了三甲医院的医生。

这个角色其实很有意思,是剧里给人形象反差最大的,前期还是个普通的邻家女孩。

再次归来后居然变成这样,架上了半包的框架眼镜,妆容上也进行了调整,用了冷白色粉底修饰肤色,橘红色系口红眼影,更显干净清冷,一下子充满了精英阶层的疏离感。
表情冷峻攻气十足,有几分神似 ” 姬圈天菜 ” 天海佑希。
论化妆和穿搭的重要性,可以达到 ” 换头 ” 的功效
后期的她穿着多是冷色系高领毛衣外搭羊绒大衣,遵循同色系配色,拎爱马仕,开 80 万的奔驰,一种 ” 低调的奢华 ” 让你在细节中知道,这大夫她不好惹(狗头)。
用颜色的深浅来制造层次感,高级感扑面而来。
这类禁欲系的穿搭,讲究的就是一种冷感,甚至包包也选同色系,不会搭配的可以用这种手法提升穿衣质感。
俄罗斯博主 Liza Rudkevich 很擅长用高级又温柔的莫兰迪色系穿搭,且单品都很日常。
金高银也是温柔色系高手。

《狂飙》的爆火再次印证了国产剧能产出佳作,包括之前的《风吹半夏》,戏中不止有许多 ” 名场面 “,连服装造型都用了心思,同样是因为角色需要使用了大量的奢侈品,但不像 F 吐槽过的《爱的二八定律》和《玫瑰之战》,原因就在于造型团队对角色在不同时期和场合的把握,可以很直观地看到人物穿衣品味的变化,而不是盲目追求彰显财富的装扮。

2023 年,期待更多国产好剧。

谢谢你喜欢我们的文章,更欢迎你后台与我交流各种时尚心得。