【大数据开发】scala——字符串差值器、正则表达式、Netty模型的Server和Client端的通信

一、String INTERPOLATION(字符串插值)

①s:字符串插值
在任何字符串前加上s,就可以直接在串中使⽤变量了
字符串插值的位置也可以放表达式

②f:插值并格式化输出
插值f 可以对字符串进⾏格式化,类似printf:
在任何字符串字⾯前加上 f,就可以⽣成简单的格式化串,功能相似于其他语⾔中的 printf 函数。

③raw:对字符串不作任何变换的输出
除了对字⾯值中的字符不做编码外,raw 插值器与 s 插值器在功能上是相同的。
raw类似于s,但是raw对字符串内容不作任何的转换

object _01_StringUsage {
    // 字符串插值
    // s""
    // f"" :
    // raw"" :
    def main(args: Array[String]): Unit = {
        val name: String = "xiaobai"
        val height: Double = 104.5

        println(s"大家好, 我叫$name, 身高是:${height}cm")
        println(f"大家好, 我叫$name, 身高是:${height}%.2fcm")
        println(raw"大家好, 我叫$name \n 身高是:${height}cm")
    }
}

二、正则表达式

Scala 通过 scala.util.matching 包中的 Regex 类来⽀持正则表达式。
scala.util.matching.Regex

  • findFirstMatchIn() 返回第⼀个匹配(Option[Match])
  • findAllMatchIn() 返回所有匹配结果(Regex.Match)
  • findAllIn() 返回所有匹配结果(String)

findFirstIn() 和findAllIn()是最常用的两个方法

实例中使⽤ String 类的 r() ⽅法构造了⼀个Regex对象。
然后使⽤ findFirstIn ⽅法找到⾸个匹配项。
如果需要查看所有的匹配项可以使⽤ findAllIn ⽅法。
可以使⽤ mkString( ) ⽅法来连接正则表达式匹配结果的字符串,并可以使⽤管道(|)来设置不同的模式

如果需要将匹配的⽂本替换为指定的关键词,可以使⽤ replaceFirstIn( ) ⽅法来替换第⼀个匹配项,使⽤ replaceAllIn( ) ⽅法替换所有匹配项

.r方法可使任意字符串变成一个正则表达式。

2.1正则匹配

2.2正则查找

2.3正则替换

2.4正则分组

import scala.util.matching.Regex

object _02_Regex {
    // 正则表达式
    // 在Scala中,使用 scala.util.matching.Regex 类描述正则
    def main(args: Array[String]): Unit = {
        // _01_basicMatch()
        // _02_regexFind()
        // _03_regexReplace()
        // _04_regexUnapply()
        // _05_regexUnapply()
        _06_regexGroups()
    }

    // 1. 正则表达式基础匹配
    def _01_basicMatch(): Unit = {
        // 用的是String类中的matches方法
        // 邮箱: xxxxx@xxxxx.xxx  需要验证 qq、126、163 邮箱
        // \w{6,12}@(qq|QQ|126|163)\.(com|cn)
        val emailStr: String = "shawn@163.com"
        val regex: String = "\\w{4,12}@(qq|QQ|126|163)\\.(com|cn)"

        println(emailStr.matches(regex))
    }

    // 2. 正则查找(查找一个字符串中,满足指定正则表达式规则的部分)
    def _02_regexFind(): Unit = {
        val str: String = "hello123world456nihao789wobuhao012"
        val regex: Regex = "\\d+".r

        // 查找第一个匹配项
        val res0: Option[String] = regex.findFirstIn(str)
        res0 match {
            case Some(n) => println(n)
            case _ => println("没有找到匹配项")
        }

        // 查找所有的匹配项
        val res1: Regex.MatchIterator = regex.findAllIn(str)
        while (res1.hasNext) {
            println(res1.next())
        }
    }

    // 3. 正则替换(替换一个字符串中满足条件的部分,为指定的部分)
    def _03_regexReplace(): Unit = {
        val str: String = "hello123world456nihao789wobuhao012"
        val regex: Regex = "\\d+".r

        println(regex.replaceFirstIn(str, "数字部分"))
        println(regex.replaceAllIn(str, "数字部分"))
        println(regex.replaceAllIn(str, matcher => {
            matcher.matched match {
                case "456" => "数字部分"
                case _ => matcher.matched
            }
            // matcher.matched
            // matcher.group()
            // matcher.start
            // matcher.end
        }))
    }

    // 4. 正则提取器(提取的是一个字符串中,每一个分组的内容)
    def _04_regexUnapply(): Unit = {
        val email: String = "shawn@163.com"
        // 需求: 需要提取这个邮箱的用户名部分(user)和服务部分(domain)
        val regex: Regex = "(\\w{4,12})@((qq|QQ|126|163)\\.(com|cn))".r

        // 注意事项:
        // case后的接收数据的变量个数,要和正则中的分组的数量一致
        email match {
            case regex(user, domain, group3, group4) =>
                println(s"user = $user, domain = $domain, group3 = $group3, group4 = $group4")
            case _=> println("正则不匹配,或者分组数量匹配错误")
        }
    }

    // 5. 提取器
    def _05_regexUnapply(): Unit = {
        // 需求: 需要提取这个邮箱的用户名部分(user)和服务部分(domain)
        class Email(var user: String, var domain: String)
        object Email {
            // 提取器
            def unapply(email: String): Option[(String, String)] = {
                if (email.matches("\\w{4,10}@(qq|QQ|126|163)\\.(com|cn)")) {
                    val parts: Array[String] = email.split("@")
                    Some(parts(0), parts(1))
                }
                else
                    None
            }
        }

        //
        "shawn@126.com" match {
            case Email(user, domain) => println(s"user = $user, domain = $domain")
            case _ => println("没有提取到数据")
        }
    }

    // 6. 正则分组
    def _06_regexGroups(): Unit = {
        val email: String = "shawn@163.com    xiaobai@126.com     xiaohui@qq.com"
        val regex: Regex = "(\\w{4,12})@((qq|QQ|126|163)\\.(com|cn))".r
        // 需求: 提取一个email中所有的分组
        val matches: Iterator[Regex.Match] = regex.findAllMatchIn(email)

        for (matcher <- matches) {
            // 获取到分组的信息
            val groupCount: Int = matcher.groupCount

            for (i <- 1 to groupCount) {
                println(s"第${i}组  ==>  ${matcher.group(i)}")
            }
            println("-----------")
        }
    }
}

三、Netty

实现Netty模型的Server和Client端的通信

  1. 创建Server端
  2. 创建Client端
  3. 实现Server端Handler
  4. 实现Client端Handler
  5. 进⾏通信

3.1ClientHander

/**
 * Netty实现客户端和服务端的通信
 *
 * 1. 导入Spark的包, 因为在Scala中是没有Netty的
 * 2. 需要创建Server端的Handler和Client端的Handler, 用来封装数据
 * 3. 需要Server端和Client端, 进行通信
 *
 * <dependency>
 *      <groupId>org.apache.spark</groupId>
 *      <artifactId>spark-core_2.11</artifactId>
 *      <version>2.2.0</version>
 * </dependency>
 * 
 * /

import io.netty.buffer.{ByteBuf, Unpooled}
import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter}

/**
 * 用于客户端的数据封装,其实是一个NIO中的Channel,数据以Buffer的形式传输
 */
class ClientHandler extends ChannelInboundHandlerAdapter {

    // 发送消息的时候使用
    override def channelActive(ctx: ChannelHandlerContext): Unit = {
        println("客户端向服务端发送数据...")
        // 要发送给服务端的数据
        val msg: String = "hello, Server"
        // 发送数据
        ctx.writeAndFlush(Unpooled.copiedBuffer(msg.getBytes("utf8")))
    }

    // 从通道中读取数据, (读取服务端发送回来的消息)
    override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
        println("读取服务端返回的数据...")
        // 读取Channel中的缓冲区
        val byteBuf: ByteBuf = msg.asInstanceOf[ByteBuf]
        // 实例化一个字节数组,用于读取缓冲区中的数据
        val bytes: Array[Byte] = new Array[Byte](byteBuf.readableBytes())
        // 将缓冲区中的数据,读取到字节数组中
        byteBuf.readBytes(bytes)
        // 将字节转成字符串
        println(new String(bytes, "utf8"))
    }


    override def channelReadComplete(ctx: ChannelHandlerContext): Unit = {
        // 如果需要循环给Server发送消息, 在这里再次调用 channelActive 就可以了
        // channelActive(ctx)
        ctx.flush()
    }
}

3.2 ServerHander

import io.netty.buffer.{ByteBuf, Unpooled}
import io.netty.channel.{ChannelHandlerContext, ChannelInboundHandlerAdapter}

class ServerHandler extends ChannelInboundHandlerAdapter {

    // 当客户端连接到Server端的时候, 调用
    override def channelActive(ctx: ChannelHandlerContext): Unit = {
        println("一个客户端连接上了")
        Thread.sleep(2000)
    }

    // 从通道中读取数据, (读取客户端发送回来的消息)
    override def channelRead(ctx: ChannelHandlerContext, msg: Any): Unit = {
        println("读取客户端发送的数据...")
        // 读取Channel中的缓冲区
        val byteBuf: ByteBuf = msg.asInstanceOf[ByteBuf]
        // 实例化一个字节数组,用于读取缓冲区中的数据
        val bytes: Array[Byte] = new Array[Byte](byteBuf.readableBytes())
        // 将缓冲区中的数据,读取到字节数组中
        byteBuf.readBytes(bytes)
        // 将字节转成字符串
        println(new String(bytes, "utf8"))

        // 回传数据给Client
        ctx.writeAndFlush(Unpooled.copiedBuffer("你好,客户端,再见!".getBytes("utf8")))
    }


    override def channelReadComplete(ctx: ChannelHandlerContext): Unit = {
        // 如果需要循环给Server发送消息, 在这里再次调用 channelActive 就可以了
        // channelActive(ctx)
        ctx.flush()
    }
}

3.3 NettyClient

import io.netty.channel.nio.NioEventLoopGroup
import io.netty.bootstrap.Bootstrap
import io.netty.channel.{ChannelFuture, ChannelHandler, ChannelHandlerContext, ChannelInitializer}
import io.netty.channel.socket.nio.NioSocketChannel

class NettyClient {
    // 连接到指定的服务端
    def connect(host: String, port: Int): Unit = {
        // 1. 配置线程组
        val group: NioEventLoopGroup = new NioEventLoopGroup()
        // 2. 创建一个客户端发送消息辅助类
        val bootstrap: Bootstrap = new Bootstrap()
        // 3. 绑定事件
        bootstrap.group(group)
            .channel(classOf[NioSocketChannel])
            // 绑定IO事件
            .handler(new ChannelInitializer[NioSocketChannel] {
                override def initChannel(c: NioSocketChannel): Unit = c.pipeline().addLast(new ClientHandler)
            })
        // 4. 绑定端口
        val future: ChannelFuture = bootstrap.connect(host, port).sync()

        future.channel().closeFuture().sync()
        group.shutdownGracefully()
    }
}

object NettyClient {
    def main(args: Array[String]): Unit = {
        val client: NettyClient = new NettyClient
        client.connect("127.0.0.1", 8888)
    }
}

3.4 NettyServer

import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.{ChannelFuture, ChannelInitializer}
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.nio.{NioServerSocketChannel, NioSocketChannel}

class NettyServer {
    def bind(host: String, port: Int): Unit = {
        // 1. 配置一个线程组
        // 1.1. 服务器接收客户端连接的
        val group: NioEventLoopGroup = new NioEventLoopGroup()
        // 1.2. 轮询每一个客户端发送的消息
        val loopGroup: NioEventLoopGroup = new NioEventLoopGroup()

        // 2. 实例化一个服务端辅助类
        val bootstrap: ServerBootstrap = new ServerBootstrap()

        // 3. 绑定线程组、Channel、事件
        bootstrap.group(group, loopGroup)
            .channel(classOf[NioServerSocketChannel])
            .childHandler(new ChannelInitializer[NioServerSocketChannel] {
                override def initChannel(c: NioServerSocketChannel): Unit = c.pipeline().addLast(new ServerHandler)
            })

        // 4. 绑定端口
        val future: ChannelFuture = bootstrap.bind(host, port).sync()

        future.channel().closeFuture().sync()
        group.shutdownGracefully()
        loopGroup.shutdownGracefully()
    }
}

object NettyServer {
    def main(args: Array[String]): Unit = {
        val server: NettyServer = new NettyServer
        server.bind("127.0.0.1", 8888)
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值