《第一行代码 第3版》学习笔记——第十一章 网络技术

1 webview用法

class MainActivity : ComponentActivity() {@SuppressLint("SetJavaScriptEnabled")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {NetWorkDemoTheme {// A surface container using the 'background' color from the themeSurface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {AndroidView(factory = { context ->WebView(context).apply {webViewClient = object : WebViewClient() {override fun shouldOverrideUrlLoading(view: WebView?,request: WebResourceRequest?): Boolean {try {if (url!!.startsWith("baiduboxapp://")) {val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))startActivity(intent)return true}} catch (e: Exception) {return false}view?.loadUrl(url!!)return true}}settings.javaScriptEnabled = trueloadUrl("https://www.baidu.com/")}})}}}}
}

Compose没有WebView控件,使用传统的WebView控件,创建一个WebViewClient对象,用于展示百度首页。loadUrl函数加载百度首页数据。javaScriptEnabled用于加载JavaScript样式
由于baidu有自定义scheme,所以这里做了特殊处理

2 使用http访问网络

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {NetWorkDemoTheme {// A surface container using the 'background' color from the themeSurface(color = MaterialTheme.colorScheme.background) {ShowHttp()}}}}
}@Composable
fun ShowHttp() {var response by remember {mutableStateOf("")}LazyColumn {item {Button(onClick = {thread {var conn: HttpURLConnection? = nulltry {val res = StringBuilder()val url = URL("https://www.baidu.com")conn = url.openConnection() as HttpURLConnectionconn.connectTimeout = 8000conn.readTimeout = 8000val input = conn.inputStreamval reader = BufferedReader(InputStreamReader(input))reader.use {reader.forEachLine {res.append(it)}}response = res.toString()Log.i("TAG", "response = $response ")} catch (e: Exception) {e.printStackTrace()} finally {conn?.disconnect()}}},modifier = Modifier.fillMaxWidth()) {Text(text = "request")}}item {Button(onClick = {thread {try {val client = OkHttpClient()val request = Request.Builder().url("https://www.baidu.com").build()val res = client.newCall(request).execute()val responseData = res.body?.string()if (responseData != null) {response = responseData}} catch (e: Exception) {e.printStackTrace()}}},modifier = Modifier.fillMaxWidth()) {Text(text = "request from okhttp")}}item {Text(text = response)}}
}

这里创建两个按钮,一个数据展示的空间。两个按钮是两种使用http访问网络的方式,第一种是Java自带的HttpURLConnection相关的API,第二种是使用okhttp这个开源框架。
下面是访问baidu之后的打印界面
在这里插入图片描述

3 解析xml数据

网络上的数据经常使用xml或json进行传输,需要学习怎么对xml和json类型数据进行解析
这个使用pull和sax方式解析xml

class MainActivity : ComponentActivity() {private final val TAG = "MainActivity"override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {NetWorkDemoTheme {// A surface container using the 'background' color from the themeSurface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {XmlPage { sendRequestForXml() }}}}}private fun sendRequestForXml() {thread {try {val client = OkHttpClient()val request = Request.Builder().url("http://192.168.12.148:12345/get_data_xml").build()val response = client.newCall(request).execute()val responseData = response.body?.string()if (responseData != null) {parseXmlDataWithPull(responseData)}if (responseData != null) {parseXmlWithSax(responseData)}} catch (e: Exception) {e.printStackTrace()}}}private fun parseXmlDataWithPull(responseData: String) {try {val factory = XmlPullParserFactory.newInstance()val xmlPullparser = factory.newPullParser()xmlPullparser.setInput(StringReader(responseData))var eventType = xmlPullparser.eventTypevar id = ""var name = ""var version = ""while (eventType != XmlPullParser.END_DOCUMENT) {val nodeName = xmlPullparser.namewhen (eventType) {XmlPullParser.START_TAG -> {when (nodeName) {"id" -> id = xmlPullparser.nextText()"name" -> name = xmlPullparser.nextText()"version" -> version = xmlPullparser.nextText()}}XmlPullParser.END_TAG -> {if ("app" == nodeName) {Log.d(TAG, "id = $id, name = $name, version = $version")}}}eventType = xmlPullparser.next()}} catch (e: Exception) {e.printStackTrace()}}private fun parseXmlWithSax(responseData: String) {try {val factory = SAXParserFactory.newInstance()val xmlReader = factory.newSAXParser().xmlReaderval handler = ContentHandler()xmlReader.contentHandler = handlerxmlReader.parse(InputSource(StringReader(responseData)))} catch (e: Exception) {e.printStackTrace()}}
}@Composable
fun XmlPage(sendRequest: () -> Unit
) {LazyColumn {item {Button(onClick = {sendRequest()},modifier = Modifier.fillMaxWidth()) {Text(text = "GetXml")}}}
}

SAX方式解析需要继承DefaultHandler并重写其中的方法

class ContentHandler : DefaultHandler() {private var nodeName = ""private lateinit var id: StringBuilderprivate lateinit var name: StringBuilderprivate lateinit var version: StringBuilderoverride fun startDocument() {id = StringBuilder()name = StringBuilder()version = StringBuilder()}override fun startElement(uri: String,localName: String,qName: String,attributes: Attributes) {nodeName = localName}override fun characters(ch: CharArray, start: Int, length: Int) {when (nodeName) {"id" -> id.appendRange(ch, start,  length)"name" -> name.appendRange(ch, start, length)"version" -> version.appendRange(ch, start, length)}Log.d("ContentHandler", "id = $id")Log.d("ContentHandler", "name = $name")Log.d("ContentHandler", "version = $version")}override fun endElement(uri: String?, localName: String?, qName: String?) {if ("app" == localName) {Log.d("endElement", "id = $id")Log.d("endElement", "name = $name")Log.d("endElement", "version = $version")Log.d("ContentHandler", "id is $id, name is $name, version is $version")id.setLength(0)name.setLength(0)version.setLength(0)}}override fun endDocument() {}
}

点击之后会打印

id = 1, name = Google Maps, version = 1.0
id = 2, name = Chrome, version = 2.1
id = 3, name = Google Play, version = 3.2

4 解析Json数据

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)sendRequestForJson()setContent {NetWorkDemoTheme {// A surface container using the 'background' color from the themeSurface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {}}}}private fun sendRequestForJson() {thread {val client = OkHttpClient()val request = Request.Builder().url("http://192.168.12.148:12345/get_data_json").build()val response = client.newCall(request).execute()val responseData = response.body?.string()if (responseData != null) {parseJsonWithJsonObject(responseData)parseJsonWithGson(responseData)}}}private fun parseJsonWithJsonObject(responseData: String) {try {val jsonArray = JSONArray(responseData)for (i in 0 until jsonArray.length()) {val jsonObject = jsonArray.getJSONObject(i)val id = jsonObject.getString("id")val name = jsonObject.getString("name")val version = jsonObject.getString("version")Log.d("parseJsonWithJsonObject","id = ${id.trim()}, name = ${name.trim()}, version = ${version.trim()}")}} catch (e: Exception) {e.printStackTrace()}}private fun parseJsonWithGson(responseData: String) {val gson = Gson()val typeOf = object : TypeToken<List<App>>() {}.typeval appList = gson.fromJson<List<App>>(responseData, typeOf)for (app in appList) {Log.d("parseJsonWithGson", "id = ${app.id.trim()}, name = ${app.name.trim()} " +", version = ${app.version.trim()}")}}
}class App(val id: String, val name: String, val version: String)

使用JSONArray和Gson解析,Gson可以直接解析成对象。
打印如下

id = 5, name = Clash of Clans, version = 5.5
id = 6, name = Boom Beach, version = 7.0
id = 7, name = Clash Royale, version = 3.5
id = 5, name = Clash of Clans , version = 5.5
id = 6, name = Boom Beach , version = 7.0
id = 7, name = Clash Royale , version = 3.5

5 使用回调

由于网络请求是耗时的操作,在子线程中操作,无法准确知道结果什么时候返回,所以可以通过回调的方式来返回结果。

HttpCallbackListener

interface HttpCallbackListener {fun onFinish(response: String)fun onError(e: Exception)
}

HttpUtils

object HttpUtils {private const val TAG = "HttpUtils"fun sendHttpRequest(address: String, listener: HttpCallbackListener) {thread {var connect: HttpURLConnection? = nulltry {val response = StringBuilder()val url = URL(address)connect = url.openConnection() as HttpURLConnectionconnect.connectTimeout = 8000connect.readTimeout = 8000val inputStream = connect.inputStreamval reader = BufferedReader(InputStreamReader(inputStream))reader.use {reader.forEachLine {response.append(it)}}listener.onFinish(response.toString())} catch (e: Exception) {e.printStackTrace()listener.onError(e)} finally {connect?.disconnect()}}}fun sendHttpRequest(address: String, callback: okhttp3.Callback) {thread {val client = OkHttpClient()val request = Request.Builder().url(address).build()client.newCall(request).enqueue(callback)}}fun parseXmlWithPull(response: String): String {try {val factory = XmlPullParserFactory.newInstance()val parser = factory.newPullParser()parser.setInput(StringReader(response))var eventType = parser.eventTypeval responseData = StringBuilder()var id = ""var name = ""var version = ""while (eventType != XmlPullParser.END_DOCUMENT) {val nodeName = parser.namewhen (eventType) {XmlPullParser.START_TAG -> {when (nodeName) {"id" -> id = nodeName"name" -> name = nodeName"version" -> version = nodeName}}XmlPullParser.END_TAG -> {if ("app" == nodeName) {val text = "id = ${id.trim()}, name = ${name.trim()}," +" version = ${version.trim()}\n"Log.d(TAG, text)responseData.append(text)}}}eventType = parser.next()}return responseData.toString()} catch (e: Exception) {e.printStackTrace()return ""}}val localIpv4Address: String?get() {val en = NetworkInterface.getNetworkInterfaces()while (en.hasMoreElements()) {val netInterface = en.nextElement()val enIpAddress = netInterface.inetAddresseswhile (enIpAddress.hasMoreElements()) {val inetAddress = enIpAddress.nextElement()if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {return inetAddress.hostAddress!!.toString()}}}return null}
}

MainActivity

const val TAG = "MainActivity"class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {NetWorkDemoTheme {// A surface container using the 'background' color from the themeSurface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {RequestPage()}}}}
}@Composable
fun RequestPage() {var result by remember {mutableStateOf("")}LazyColumn {result = ""item {Button(onClick = {val listener = object : HttpCallbackListener {override fun onFinish(response: String) {result = "HttpURLConnection data: \n"result += HttpUtils.parseXmlWithPull(response)}override fun onError(e: Exception) {Log.d(TAG, "onError: ")result = "HttpURLConnection request failed"}}val ip = HttpUtils.localIpv4Addressval url = "http://$ip:12345/get_data_xml"HttpUtils.sendHttpRequest(url, listener)},modifier = Modifier.fillMaxWidth()) {Text(text = "request for xml with HttpURLConnection")}}item {Button(onClick = {result = ""val callback = object : Callback {override fun onFailure(call: Call, e: IOException) {Log.d(TAG, "onFailure: ")result = "okhttp request failed"}override fun onResponse(call: Call, response: Response) {result = "okhttp data: \n"result += HttpUtils.parseXmlWithPull(response.body?.string().toString())}}val ip = HttpUtils.localIpv4Addressval url = "http://$ip:12345/get_data_xml"HttpUtils.sendHttpRequest(url, callback)},modifier = Modifier.fillMaxWidth()) {Text(text = "request for xml with okhttp")}}item {Text(text = result)}}
}

这里展示了HttpURLConnection和okhttp使用回调的方式,HttpURLConnection需要自己创建回调接口,okhttp则有自带的callback接口。

6 Retrofit

6.1 Retrofit使用

Retrofit基于以下几点设计:

  • 同一款应用程序中所发起的网络请求绝大多数指向的是同一个服务器域名
  • 服务器提供的接口通常是可以根据功能来归类的
  • 开发者肯定更加习惯于“调用一个接口,获取它的返回值”这样的编码方式
class App(val id: String, val name: String, val version: String)

创建一个App类用于存储数据,Retrofit可以通过Gson直接将xml数据解析成对象进行存储

interface AppService {@GET("get_data_json")fun getAppData(): Call<List<App>>
}

创建一个AppService 接口,定义一个函数做网络请求的入口,使用GET注解表示一个是get类型的请求,由于Retrofit可以设置baseurl,所以这里只需要设置相对的资源路径

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {NetWorkDemoTheme {// A surface container using the 'background' color from the themeSurface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {GetXmlData()}}}}
}@Composable
fun GetXmlData() {var xmlData by remember {mutableStateOf("")}LazyColumn {item {Button(onClick = {val ip = HttpUtils.localIpv4Addressval retrofit = Retrofit.Builder().baseUrl("http://$ip:12345/").addConverterFactory(GsonConverterFactory.create()).build()val appService = retrofit.create(AppService::class.java)appService.getAppData().enqueue(object : Callback<List<App>> {override fun onResponse(call: Call<List<App>>,response: Response<List<App>>) {val result = StringBuilder()val list = response.body()if (list != null) {for (app in list) {result.append(Gson().toJson(app).toString() + "\n")}}xmlData = result.toString()}override fun onFailure(call: Call<List<App>>, t: Throwable) {xmlData = "request failed"}})},modifier = Modifier.fillMaxWidth()) {Text(text = "get xml data")}}item {Text(text = xmlData)}}
}

首先创建Retrofit对象,设置baseurl,并设置Gson作为转换工具。
然后创建AppService子类对象,调用getAppData方法,并调用enqueue开始发起网络请求。后面传入一个回调作为参数,请求的response返回后直接触发回调
请求后效果如下:
在这里插入图片描述

6.2 其他请求方式

如果需要复杂参数传递,可以参考

// GET http://example.com/<page>/get_data.json
interface ExampleService {@GET("{page}/get_data.json")fun getData(@Path("page") page: Int): Call<Data>
}

如果存在页面参数,可以通过传入一个int值并使用Path注解修饰

// GET http://example.com/get_data.json?u=<user>&t=<token>
interface ExampleService {@GET("get_data.json")fun getData(@Query("u") user: String, @Query("t") token: String): Call<Data>
}

这个是带参数查询的写法

// DELETE http://example.com/data/<id>
interface ExampleService {
@DELETE("data/{id}")fun deleteData(@Path("id") id: String): Call<ResponseBody>
}

这个是delete请求

// POST http://example.com/data/create {"id": 1, "content": "The description for this data."}
interface ExampleService {@POST("data/create")fun createData(@Body data: Data): Call<ResponseBody>
}

这个是post请求

// GET http://example.com/get_data.json
// User-Agent: okhttp
// Cache-Control: max-age=0
interface ExampleService {@Headers("User-Agent: okhttp", "Cache-Control: max-age=0")@GET("get_data.json")fun getData(): Call<Data>
}

这种是静态的Header中添加数据

interface ExampleService {@GET("get_data.json")fun getData(@Header("User-Agent") userAgent: String,@Header("Cache-Control") cacheControl: String): Call<Data>
}

这种是动态的Header中添加数据

6.3 最佳实践

val ip = HttpUtils.localIpv4Address
val retrofit = Retrofit.Builder().baseUrl("http://$ip:12345/").addConverterFactory(GsonConverterFactory.create()).build()
val appService = retrofit.create(AppService::class.java)

这段代码可以优化,创建一个ServiceCreator 类

object ServiceCreator {private const val BASE_URL = "http://192.168.12.148:12345"private val retrofit = Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build()fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)inline fun <reified T> create(): T = create(T::class.java)
}

使用泛型,并创建一个内联函数,使用reified修饰,可以访问泛型的真实类型来进一步简化

// val appService = ServiceCreator.create(AppService::class.java)
val appService = ServiceCreator.create<AppService>()

没有加入inline函数可以调用上面的,加入inline之后,更为简化

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

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

相关文章

主流MQ对比和选型

在以下几个我们比较关心的维度进行对比 ActiveMQ RabbitMQ RocketMQkafka官网https://activemq.apache.org/https://www.rabbitmq.com/https://rocketmq.apache.org/https://kafka.apache.org/githubhttps://github.com/apache/activemqhttps://github.com/rabbitmqhttps://g…

AI如何让办公更智能?WPS AI海外版给出答案

导读&#xff1a;从语义检查到一键生成PPT&#xff0c;WPS Office海外版如何面向2亿月活用户快速推出AI功能&#xff1f; 近日&#xff0c;WPS Office海外版应用亚马逊云科技Amazon Bedrock等生成式AI技术与服务&#xff0c;在海外正式推出人工智能应用WPS AI海外版&#xff0c…

Postman测试,如何保持用户登录状态?

为了在Postman中保持用户登录状态&#xff0c;我们可以使用以下步骤&#xff1a; 1. 下载和安装Postman 首先&#xff0c;我们需要下载和安装Postman。Postman是一个流行的API开发和测试工具&#xff0c;可以帮助我们发送HTTP请求并测试API的功能。 2. 创建一个新的Postman …

【Vue】vue-router路由使用

前言 Vue Router是Vue框架中非常重要的一个功能。 目标 1 单页面应用与多页面应用的区别; 2 vue-router的具体实现方法; 3 路由模式有哪几种,有什么区别; 4 如何进行路由守卫与路由缓存; 一 路由的概念 概念 Vue Router是Vue提供的路由管理器。将组件与路由一一对应起来,…

6-2 归并排序

6-2 归并排序 分数 10 全屏浏览 切换布局 作者 软件工程DS&A课程组 单位 燕山大学 以下代码采用分而治之算法实现归并排序。请补充函数mergesort&#xff08;&#xff09;的代码。提示&#xff1a;mergesort&#xff08;&#xff09;函数可用递归实现&#xff0c;其中参…

Conda创建与激活虚拟环境(指定虚拟环境创建位置)

1.Conda优势 Conda是一个开源的软件包管理系统和环境管理系统&#xff0c;主要用于在不同的计算环境中安装和管理软件包和其依赖项。它最初是为Python而设计的&#xff0c;但现在也可以用于管理其他语言的软件包。 Conda提供了对虚拟环境的支持&#xff0c;这使得用户可以在同…

如何在Java中处理UnsupportedOperationException异常?

如何在Java中处理UnsupportedOperationException异常&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;我们经常会遇到各…

swiper实例

大家好&#xff0c;我是燐子&#xff0c;今天给大家带来swiper实例 微信小程序中的 swiper 组件是一种用于创建滑动视图的容器组件&#xff0c;常用于实现图片轮播、广告展示等效果。它通过一系列的子组件 swiper-item 来定义滑动视图的每一个页面。 基本用法 以下是一个简单的…

ESAPI.setAttribute设置值前端取不到

我在后端使用java设置email request.setAttribute("email",ESAPI.encoder().encodeForHTML("123456qq.com"))前端jsp页面获取不到&#xff0c; var email"<%ESAPI.encoder().encodeForHTML(request.getParameter("email"))%>"…

web前端——HTML

目录 一、HTML概述 1.HTML是什么&#xff1f; 2.HTML具体化解释 二、HTML基本语法 1.声明 2. Head头标签 3.body身体标签 4.一个html的基本结构 5.标签 6.标签属性 ①属性的格式 ②属性的位置 ③添加多个属性 三、基本常用标签 1.超链接 2.图像标签 ①图像标…

springboot集成JPA并配置hikariCP连接池问题解决

一、引入需要的依赖 springboot版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-parent</artifactId><version>2.3.2.RELEASE</version><relativePath/></parent> jpa依赖 <!--…

从零开始做题:会打篮球的鸡

会打篮球的鸡 1 题目 给你password你帮鸡肋找找会打篮球的鸡在哪儿行吗&#xff1f; password:iVBORw0KGgoAAAANSUhEUgAAAgAAAPoCAIAAADCwUOzAAAACXBIWXMAAAsTAAALEwEAmpwYAAB2KElEQVR4nO3dd3xb1f3/8WvLe8QjdpbtxJm2Eyd29t6LJBBWgEICFAqUUmaBlrZ8Ke23fLFUvYsYRQKZScECGQHkpC9…

OpenGL进阶系列1 - OpenGL1.x和2.x功能演进(上古历史)

时间版本功能详细描述1992v1.0 NewList/EndList/CallListglspec10.pdfBegin/Endglspec10.pdfVertex/TexCoord/Color/Normal/Index/Rectglspec10.pdfMatrixMode/LoadMatrix/Multmatrixglspec10.pdfRoate/Translate/Scaleglspec10.pdf

1964springboot VUE小程序在线学习管理系统开发mysql数据库uniapp开发java编程计算机网页源码maven项目

一、源码特点 springboot VUE uniapp 小程序 在线学习管理系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架uniapp和VUE完成本系统&#xff0c;对理解vue java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09;&#xff0c;…

DLS平台:GPT-5预计于2025年底至2026年初发布,将实现“博士水平”智能

摘要 OpenAI首席技术官Mira Murati近日透露&#xff0c;GPT-5可能推迟到2025年底或2026年初发布。这一消息打破了市场对GPT-5在2023年底或2024年夏季发布的预期。尽管推迟&#xff0c;但GPT-5将实现显著的性能飞跃&#xff0c;在特定任务中达到“博士水平”的智能。这标志着人…

Java 8 Date and Time API

Java 8引入了新的日期和时间API&#xff0c;位于java.time包下&#xff0c;旨在替代旧的java.util.Date和java.util.Calendar类。新API更为简洁&#xff0c;易于使用&#xff0c;并且与Joda-Time库的一些理念相吻合。以下是Java 8 Date and Time API中几个核心类的简要概述&…

[modern c++][11/14] 变参模板的使用

前言&#xff1a; c 11 引入和变参模板用来处理任意数量模板参数的场景。 变参模板函数 &#xff08;C11/14 迭代展开 | 一个模板参数和一个模板参数包&#xff09; #include <iostream> #include <string>void MyPrint(){std::cout << " end" …

JDBC的概念 ,核心API的介绍 , 注册驱动介绍

第一章 JDBC 1、JDBC的概念 目标 能够掌握JDBC的概念能够理解JDBC的作用 讲解 客户端操作MySQL数据库的方式 使用第三方客户端来访问MySQL&#xff1a;SQLyog、Navicat 使用MySQL自带的命令行方式 通过Java来访问MySQL数据库&#xff0c;今天要学习的内容 如何通过Java代…

ubuntu server 24.04 使用记录

我安装 Ubuntu server 24.04 选择了 minimal 方式&#xff0c;发现不知道是忘记选了还是怎样&#xff0c;ssh 无法登录。 本来以为 24.04 上只会遇到和 22.04 上一样的问题&#xff0c;校网需要验证。经过几周分析研究&#xff0c;终于摸清楚了校网验证过程&#xff0c;然后写…

sed和awk

sed和awk 文章目录 sed和awksedawk grep就是查找文件中的内容&#xff0c;扩展正则表达式 sed sed是一种流编辑器&#xff0c;一次处理一行内容&#xff08;增删改查&#xff09; 如果只是展示&#xff0c;会放在缓冲区&#xff08;模式空间&#xff09;&#xff0c;展示结束…