[置顶] [实践ok]CentOS 7 使用阿里云的yum源
Unix/LinuxC技术 jackxiang 2016-8-29 19:35
[置顶] [实践OK]vim 窗口分割技巧,vnew打开实践提高速度。
Unix/LinuxC技术 jackxiang 2008-4-2 13:43
[实践OK] GO语言中Goroutine 泄露的学习,以及在Go语言中,整个 main 函数中的代码都是在主 goroutine 中执行的。主 goroutine 负责启动程序的执行,并处理后续的逻辑,所以需要用var wg sync.WaitGroup // 创建 WaitGroup,不能提前退出而确保主 goroutine 等待所有子 goroutine 完成。如果不使用这种方式,主 goroutine 可能在子 goroutine 执行之前就结束,导致某些任务未能完成。
Php/Js/Shell/Go jackxiang 2024-10-8 11:50
加密日志
FreeBSD from 14.0 Upgrading to 14.1
Unix/LinuxC技术 jackxiang 2024-8-16 09:40
1. Made sure everything was updated and did; freebsd-update fetch & freebsd-update install (nothing to do becuase I was up to date).
2. freebsd-update -r 14.1-RELEASE upgrade
3. freebsd-update install
4. shutdown -r now
5. freebsd-update install
6. shutdown -r now (maybe overkill)
7. pkg-static upgrade -f
8. freebsd-update install (nothing to do)
9. shutdown -r now
freebsd-version -kru gave me;
14.1-RELEASE
14.0-RELEASE
14.1-RELEASE
Then I did freebsd-update fetch & freebsd-update install and the system reverted back to 14.0
来自:https://forums.freebsd.org/threads/upgrading-to-14-1.93700/
实践如下:
freebsd-update fetch & freebsd-update install
[root@core ~]# sudo freebsd-update install
No updates are available to install.
Run '/usr/sbin/freebsd-update fetch' first.
[root@core ~]# uname -rasp
FreeBSD core.hatch.l.lw4.cn 14.0-RELEASE-p9 FreeBSD 14.0-RELEASE-p9 #0: Tue Aug 6 19:52:06 UTC 2024 root@amd64-builder.daemonology.net:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64 amd64
[root@core ~]# freebsd-update fetch
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching metadata signature for 14.0-RELEASE from update1.freebsd.org... done.
Fetching metadata index... done.
Inspecting system... done.
Preparing to download files... done.
No updates needed to update system to 14.0-RELEASE-p9.
You have new mail in /var/mail/root
[root@core ~]# freebsd-update -r 14.1-RELEASE upgrade
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching metadata signature for 14.0-RELEASE from update1.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata files... done.
Inspecting system... done.
The following components of FreeBSD seem to be installed:
kernel/generic kernel/generic-dbg src/src world/base world/lib32
The following components of FreeBSD do not seem to be installed:
world/base-dbg world/lib32-dbg
Does this look reasonable (y/n)? y
Fetching metadata signature for 14.1-RELEASE from update1.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata patches. done.
Applying metadata patches... done.
Fetching 1 metadata files...
6490....6500....6510....6520....6530....6540....6550....6560....6570....6580....6590....6600....6610....6620....6630....6640....6650....6660....6670....6680....6690....6700....6710....6720....6730....6740....6750....6760....6770....6780....6790....6800....6810....6820....6830....6840....6850....6860....6870....6880....6890....6900....6910....6920....6930....6940....6950.Timeout, server 10.10.0.1 not responding.
Freebsd升级到这一步,不想升级了,清理掉产生的内容,怎么弄?
freebsd-update rollback
rm -rf /var/db/freebsd-update/*
rm -rf /usr/freebsd-update/*
2. freebsd-update -r 14.1-RELEASE upgrade
3. freebsd-update install
4. shutdown -r now
5. freebsd-update install
6. shutdown -r now (maybe overkill)
7. pkg-static upgrade -f
8. freebsd-update install (nothing to do)
9. shutdown -r now
freebsd-version -kru gave me;
14.1-RELEASE
14.0-RELEASE
14.1-RELEASE
Then I did freebsd-update fetch & freebsd-update install and the system reverted back to 14.0
来自:https://forums.freebsd.org/threads/upgrading-to-14-1.93700/
实践如下:
freebsd-update fetch & freebsd-update install
[root@core ~]# sudo freebsd-update install
No updates are available to install.
Run '/usr/sbin/freebsd-update fetch' first.
[root@core ~]# uname -rasp
FreeBSD core.hatch.l.lw4.cn 14.0-RELEASE-p9 FreeBSD 14.0-RELEASE-p9 #0: Tue Aug 6 19:52:06 UTC 2024 root@amd64-builder.daemonology.net:/usr/obj/usr/src/amd64.amd64/sys/GENERIC amd64 amd64
[root@core ~]# freebsd-update fetch
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching metadata signature for 14.0-RELEASE from update1.freebsd.org... done.
Fetching metadata index... done.
Inspecting system... done.
Preparing to download files... done.
No updates needed to update system to 14.0-RELEASE-p9.
You have new mail in /var/mail/root
[root@core ~]# freebsd-update -r 14.1-RELEASE upgrade
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching metadata signature for 14.0-RELEASE from update1.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata files... done.
Inspecting system... done.
The following components of FreeBSD seem to be installed:
kernel/generic kernel/generic-dbg src/src world/base world/lib32
The following components of FreeBSD do not seem to be installed:
world/base-dbg world/lib32-dbg
Does this look reasonable (y/n)? y
Fetching metadata signature for 14.1-RELEASE from update1.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata patches. done.
Applying metadata patches... done.
Fetching 1 metadata files...
6490....6500....6510....6520....6530....6540....6550....6560....6570....6580....6590....6600....6610....6620....6630....6640....6650....6660....6670....6680....6690....6700....6710....6720....6730....6740....6750....6760....6770....6780....6790....6800....6810....6820....6830....6840....6850....6860....6870....6880....6890....6900....6910....6920....6930....6940....6950.Timeout, server 10.10.0.1 not responding.
Freebsd升级到这一步,不想升级了,清理掉产生的内容,怎么弄?
freebsd-update rollback
rm -rf /var/db/freebsd-update/*
rm -rf /usr/freebsd-update/*
[实践OK]远程桌面连接linux/centos服务器时,画面突然放大,画面随光标移动
Unix/LinuxC技术 jackxiang 2024-7-4 11:18
远程桌面连接linux服务器时,画面突然放大,且画面随光标移动。
解决方法
Alt + 鼠标滑轮
Alt + 鼠标滑轮可以进行画面的放大、缩小。有的时候不注意按到键盘,可能出现这种情况。特此记录。
摘自:https://blog.csdn.net/ykwjt/article/details/127451351
macbook下面是option+鼠标滑轮(苹果鼠标二手指前后移动=远程屏幕放大缩小)
解决方法
Alt + 鼠标滑轮
Alt + 鼠标滑轮可以进行画面的放大、缩小。有的时候不注意按到键盘,可能出现这种情况。特此记录。
摘自:https://blog.csdn.net/ykwjt/article/details/127451351
macbook下面是option+鼠标滑轮(苹果鼠标二手指前后移动=远程屏幕放大缩小)
1.布局:
切换到 Side By Side Layout,这个布局,更方便操作。
界面主要包括:正则表达式、测试文本、输出结果、历史记录、Create相关面板,见上图。
2.List All:输出结果面板,显示的显示了正则表达式的匹配结果。你可以通过List All切换显示的效果,如下图所示:
可以选中List ALL Group Matches in columns显示的效果,就是上面图中的输出结果面板效果。如果,你选中了Update Automatically,则修改正则或文本内容时候,输出结果面板会实时跟着变化。
3.高亮:
点:Highlight,打开高亮。
4.Debug:
点击 Debug按钮,会自动跳到Debug面板,显示出正则表达式匹配的规则。通过该结果,你可以很直观的分析出正则匹配的过程,如下图所示:
5.一行还是多行匹配:
测试文本面板(Test标签)输入需要测试的文本内容,如:。可以选择Line By Line、Whole File、Page By Page,来指定是每行抽取,还是对整个文档抓取等(如果你的正则匹配信息有包含多行内容,务必要选中Whole File或者Page By Page)。
来自:https://jingyan.baidu.com/article/63acb44a2d9c7761fcc17ebd.html
正则工具:https://regex101.com/
切换到 Side By Side Layout,这个布局,更方便操作。
界面主要包括:正则表达式、测试文本、输出结果、历史记录、Create相关面板,见上图。
2.List All:输出结果面板,显示的显示了正则表达式的匹配结果。你可以通过List All切换显示的效果,如下图所示:
可以选中List ALL Group Matches in columns显示的效果,就是上面图中的输出结果面板效果。如果,你选中了Update Automatically,则修改正则或文本内容时候,输出结果面板会实时跟着变化。
3.高亮:
点:Highlight,打开高亮。
4.Debug:
点击 Debug按钮,会自动跳到Debug面板,显示出正则表达式匹配的规则。通过该结果,你可以很直观的分析出正则匹配的过程,如下图所示:
5.一行还是多行匹配:
测试文本面板(Test标签)输入需要测试的文本内容,如:。可以选择Line By Line、Whole File、Page By Page,来指定是每行抽取,还是对整个文档抓取等(如果你的正则匹配信息有包含多行内容,务必要选中Whole File或者Page By Page)。
来自:https://jingyan.baidu.com/article/63acb44a2d9c7761fcc17ebd.html
正则工具:https://regex101.com/
iOS 系统自带的“相机”应用无法进行删除,也就是说正常情况下不会丢失该功能。如果您无法在 iPhone 桌面上找到“相机”应用,可以从资源库中找到并移回至桌面,或检查“屏幕使用时间”设置。
1.打开资源库查找“相机”:
在 iPhone 桌面上连续向左轻扫,直至看到“应用资源库”,在资源库中找到“相机”图标,长按拖回到桌面,放置到理想的位置即可。
来自:https://g.pconline.com.cn/x/1585/15859231.html
1.打开资源库查找“相机”:
在 iPhone 桌面上连续向左轻扫,直至看到“应用资源库”,在资源库中找到“相机”图标,长按拖回到桌面,放置到理想的位置即可。
来自:https://g.pconline.com.cn/x/1585/15859231.html
[实践OK]PHP正则实现天气预报UL表格及其里面天气信息内容获取。
Php/Js/Shell/Go jackxiang 2024-6-18 15:58
捕获组与非捕获组概念:https://segmentfault.com/a/1190000021043947
文章中的工具用的正则表达式公式: https://regex101.com/
非捕获部分:(?:.*?<span title="(.*?)" .*?<\/span>)?
对天气中的温度和风力有时间只有一个温度和风力的情况的判断算法:
温度部分:
$temperature = '';
if (preg_match('/<span>(.*?)<\/span>\/<i>(.*?)<\/i>/', $matches[4], $temp_matches)) {
$temperature = $temp_matches[1] . '/' . $temp_matches[2];
} else if (preg_match('/<i>(.*?)<\/i>/', $matches[4], $temp_matches)) {
$temperature = $temp_matches[1];
}
风力部分:
问ChatGPT:我只想知道正则如何匹配到下面这段的:
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i><3级</i>
</p>
对下面这段正则作下解释:
<p class="win">.*?<span title="(.*?)" .*?<\/span>(?:.*?<span title="(.*?)" .*?<\/span>)?
上面第一个.*?是非贪婪匹配它后面的:<span title=。
上在第二个(.*?)是匹配南风。
上在第三个.*?是非贪婪匹配它后面的:<\/span>。
是对的吧?
二、最想知道的是:
(?:.?<span title="(.?)" .?</span>)?
上在这段正则里面的:?:.? 是想干嘛的呢?
它最后的问号是用来匹配0次或1次用的,这个是这么理解吧。
ChatGPT回答如下:
你说得基本是对的。让我逐步解释一下你提到的正则表达式是如何匹配 `<p class="win">` 这段 HTML 代码的,以及 `(?:.*?<span title="(.*?)" .*?<\/span>)?` 这部分正则表达式的具体作用。
### 1. 正则表达式整体
```regex
<p class="win">.*?<span title="(.*?)" .*?<\/span>(?:.*?<span title="(.*?)" .*?<\/span>)?
```
#### 1.1. `<p class="win">.*?<span title="(.*?)" .*?<\/span>`
- `<p class="win">`: 匹配 `<p>` 标签的开头部分,class 属性为 `win`。
- `.*?`: 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
- `<span title="(.*?)"`: 匹配第一个 `span` 标签,并捕获 `title` 属性的值。`(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容,这里会匹配到 `南风`。
- ` .*?<\/span>`: 非贪婪模式匹配任意字符,直到遇到 `</span>`。
这一部分的正则表达式解释如下:
- `<p class="win">` 精确匹配标签的开始部分。
- `.*?` 尽量少的匹配任意字符,直到遇到 `<span title=`。
- `<span title="(.*?)"` 匹配 `span` 标签的 `title` 属性,捕获组 `(.*?)` 非贪婪模式匹配 `title` 的内容,即 `南风`。
- ` .*?<\/span>` 匹配从 `title` 属性结束到 `</span>` 标签结束的所有字符。
#### 1.2. `(?:.*?<span title="(.*?)" .*?<\/span>)?`
- `(?: ... )`: 非捕获组,匹配括号内的内容,但不捕获它。
- `.*?`: 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
- `<span title="(.*?)"`: 匹配第二个 `span` 标签,并捕获 `title` 属性的值。`(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容,这里会匹配到第二个 `南风`。
- ` .*?<\/span>`: 非贪婪模式匹配任意字符,直到遇到 `</span>`。
- `?`: 匹配前面的非捕获组 0 次或 1 次。即这部分是可选的。
这一部分的正则表达式解释如下:
- `(?: ... )` 表示一个非捕获组,不会保存匹配结果。
- `.*?` 尽量少的匹配任意字符,直到遇到 `<span title=`。
- `<span title="(.*?)"` 匹配 `span` 标签的 `title` 属性,捕获组 `(.*?)` 非贪婪模式匹配 `title` 的内容,即第二个 `南风`。
- ` .*?<\/span>` 匹配从 `title` 属性结束到 `</span>` 标签结束的所有字符。
- `?` 表示这个非捕获组是可选的,可以匹配 0 次或 1 次。
### 2. 匹配示例
以你提供的 HTML 代码为例:
```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i><3级</i>
</p>
```
- `<p class="win">`: 匹配 `<p class="win">`。
- `.*?`: 非贪婪模式匹配任意字符,直到遇到 `<span title=`。匹配到 `\n<em>\n`。
- `<span title="(.*?)"`: 匹配 `<span title="南风"` 并捕获 `title` 属性的值 `南风`。
- ` .*?<\/span>`: 匹配 `class="S"></span>\n`。
接下来是可选部分:
- `(?:.*?<span title="(.*?)" .*?<\/span>)?`:
- `.*?`: 非贪婪模式匹配任意字符,直到遇到 `<span title=`。匹配到 `\n`。
- `<span title="(.*?)"`: 匹配 `<span title="南风"` 并捕获 `title` 属性的值 `南风`。
- ` .*?<\/span>`: 匹配 `class="S"></span>\n`。
- `?`: 表示这个部分是可选的,可以匹配 0 次或 1 次。这次匹配到 1 次。
### 总结
- `<p class="win">.*?<span title="(.*?)" .*?<\/span>`: 匹配 `<p class="win">` 开始标签,直到第一个 `span` 标签的 `title` 属性,并捕获其值。
- `(?:.*?<span title="(.*?)" .*?<\/span>)?`: 匹配从第一个 `span` 标签结束到第二个 `span` 标签结束的内容,并捕获第二个 `span` 标签的 `title` 属性,整个部分是可选的。
- `?` 表示前面的非捕获组是可选的,可以出现 0 次或 1 次。
再对正则知识点细节性问:
(?: ... ) 表示一个非捕获组,不会保存匹配结果。
上面这个是什么意思,请再接合解释下面正则:
(?:.?<span title="(.?)" .*?</span>)?
ChatGPT回答:
好的,让我们详细解释一下 `(?: ... )` 和具体的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?`。
### 非捕获组 `(?: ... )`
- `(?: ... )` 是一个**非捕获组**,它的作用是将括号内的内容进行分组,但不保存这个组的匹配结果。这和普通的捕获组 `(...)` 不同,普通捕获组会保存匹配结果供后续引用,而非捕获组则不会保存。
- 非捕获组主要用于在需要进行分组但不希望保存匹配结果的情况下,这样可以节省内存,并且在使用正则表达式时不需要担心额外的捕获组会影响捕获组编号。
### 具体的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?`
#### 分解解释
1. **`(?: ... )`**:
- 这是一个非捕获组,括号内的内容会被当作一个整体来处理,但不会保存匹配结果。
2. **`.*?`**:
- 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
- 这里匹配的是从 `<p class="win">` 到第一个 `<span title="...">` 之间的任意字符,包括换行符等。
3. **`<span title="(.*?)"`**:
- 匹配 `<span title="` 字符串,并捕获 `title` 属性的值。
- `(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容。例如,`南风`。
4. **` .*?<\/span>`**:
- 非贪婪模式匹配任意字符,直到遇到 `</span>`。
- 这里匹配的是从 `title` 属性结束到 `</span>` 标签结束的所有字符。
5. **`(?: ... )?`**:
- 问号 `?` 表示前面的非捕获组是可选的,可以匹配 0 次或 1 次。
- 整个非捕获组 `(?: ... )` 是可选的,意味着可以有一个额外的 `span` 标签,或者没有。
#### 具体示例
以你的 HTML 代码为例:
```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i><3级</i>
</p>
```
匹配过程如下:
1. **第一部分** `<p class="win">.*?<span title="(.*?)" .*?<\/span>`:
- 匹配 `<p class="win">` 开始标签。
- `.*?` 非贪婪模式匹配任意字符,直到遇到第一个 `<span title=`。
- 匹配并捕获第一个 `span` 标签的 `title` 属性值 `南风`。
- 匹配 `</span>` 标签。
2. **第二部分** `(?:.*?<span title="(.*?)" .*?<\/span>)?`:
- `(?: ... )` 非捕获组,将内容作为整体处理。
- `.*?` 非贪婪模式匹配任意字符,直到遇到第二个 `<span title=`。
- 匹配并捕获第二个 `span` 标签的 `title` 属性值 `南风`。
- 匹配 `</span>` 标签。
- `?` 表示非捕获组是可选的,可以匹配 0 次或 1 次。
### 完整正则表达式的匹配过程
完整的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?` 在匹配过程中的作用如下:
1. 从 `<p class="win">` 开始,匹配任意字符直到第一个 `<span title=`。
2. 捕获第一个 `span` 标签的 `title` 属性值。
3. 匹配 `</span>` 标签。
4. 非捕获组 `(?: ... )?` 的内容(可选):
- 匹配任意字符直到第二个 `<span title=`。
- 捕获第二个 `span` 标签的 `title` 属性值。
- 匹配 `</span>` 标签。
这段正则表达式会成功匹配以下内容,并分别捕获两个 `span` 标签的 `title` 属性值:
```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i><3级</i>
</p>
```
结果是捕获两个 `span` 标签的 `title` 属性值 `南风` 和 `南风`。如果只有一个 `span` 标签,那么也能成功匹配,只会捕获第一个 `title` 属性值。
文章中的工具用的正则表达式公式: https://regex101.com/
非捕获部分:(?:.*?<span title="(.*?)" .*?<\/span>)?
对天气中的温度和风力有时间只有一个温度和风力的情况的判断算法:
温度部分:
$temperature = '';
if (preg_match('/<span>(.*?)<\/span>\/<i>(.*?)<\/i>/', $matches[4], $temp_matches)) {
$temperature = $temp_matches[1] . '/' . $temp_matches[2];
} else if (preg_match('/<i>(.*?)<\/i>/', $matches[4], $temp_matches)) {
$temperature = $temp_matches[1];
}
风力部分:
问ChatGPT:我只想知道正则如何匹配到下面这段的:
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i><3级</i>
</p>
对下面这段正则作下解释:
<p class="win">.*?<span title="(.*?)" .*?<\/span>(?:.*?<span title="(.*?)" .*?<\/span>)?
上面第一个.*?是非贪婪匹配它后面的:<span title=。
上在第二个(.*?)是匹配南风。
上在第三个.*?是非贪婪匹配它后面的:<\/span>。
是对的吧?
二、最想知道的是:
(?:.?<span title="(.?)" .?</span>)?
上在这段正则里面的:?:.? 是想干嘛的呢?
它最后的问号是用来匹配0次或1次用的,这个是这么理解吧。
ChatGPT回答如下:
你说得基本是对的。让我逐步解释一下你提到的正则表达式是如何匹配 `<p class="win">` 这段 HTML 代码的,以及 `(?:.*?<span title="(.*?)" .*?<\/span>)?` 这部分正则表达式的具体作用。
### 1. 正则表达式整体
```regex
<p class="win">.*?<span title="(.*?)" .*?<\/span>(?:.*?<span title="(.*?)" .*?<\/span>)?
```
#### 1.1. `<p class="win">.*?<span title="(.*?)" .*?<\/span>`
- `<p class="win">`: 匹配 `<p>` 标签的开头部分,class 属性为 `win`。
- `.*?`: 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
- `<span title="(.*?)"`: 匹配第一个 `span` 标签,并捕获 `title` 属性的值。`(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容,这里会匹配到 `南风`。
- ` .*?<\/span>`: 非贪婪模式匹配任意字符,直到遇到 `</span>`。
这一部分的正则表达式解释如下:
- `<p class="win">` 精确匹配标签的开始部分。
- `.*?` 尽量少的匹配任意字符,直到遇到 `<span title=`。
- `<span title="(.*?)"` 匹配 `span` 标签的 `title` 属性,捕获组 `(.*?)` 非贪婪模式匹配 `title` 的内容,即 `南风`。
- ` .*?<\/span>` 匹配从 `title` 属性结束到 `</span>` 标签结束的所有字符。
#### 1.2. `(?:.*?<span title="(.*?)" .*?<\/span>)?`
- `(?: ... )`: 非捕获组,匹配括号内的内容,但不捕获它。
- `.*?`: 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
- `<span title="(.*?)"`: 匹配第二个 `span` 标签,并捕获 `title` 属性的值。`(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容,这里会匹配到第二个 `南风`。
- ` .*?<\/span>`: 非贪婪模式匹配任意字符,直到遇到 `</span>`。
- `?`: 匹配前面的非捕获组 0 次或 1 次。即这部分是可选的。
这一部分的正则表达式解释如下:
- `(?: ... )` 表示一个非捕获组,不会保存匹配结果。
- `.*?` 尽量少的匹配任意字符,直到遇到 `<span title=`。
- `<span title="(.*?)"` 匹配 `span` 标签的 `title` 属性,捕获组 `(.*?)` 非贪婪模式匹配 `title` 的内容,即第二个 `南风`。
- ` .*?<\/span>` 匹配从 `title` 属性结束到 `</span>` 标签结束的所有字符。
- `?` 表示这个非捕获组是可选的,可以匹配 0 次或 1 次。
### 2. 匹配示例
以你提供的 HTML 代码为例:
```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i><3级</i>
</p>
```
- `<p class="win">`: 匹配 `<p class="win">`。
- `.*?`: 非贪婪模式匹配任意字符,直到遇到 `<span title=`。匹配到 `\n<em>\n`。
- `<span title="(.*?)"`: 匹配 `<span title="南风"` 并捕获 `title` 属性的值 `南风`。
- ` .*?<\/span>`: 匹配 `class="S"></span>\n`。
接下来是可选部分:
- `(?:.*?<span title="(.*?)" .*?<\/span>)?`:
- `.*?`: 非贪婪模式匹配任意字符,直到遇到 `<span title=`。匹配到 `\n`。
- `<span title="(.*?)"`: 匹配 `<span title="南风"` 并捕获 `title` 属性的值 `南风`。
- ` .*?<\/span>`: 匹配 `class="S"></span>\n`。
- `?`: 表示这个部分是可选的,可以匹配 0 次或 1 次。这次匹配到 1 次。
### 总结
- `<p class="win">.*?<span title="(.*?)" .*?<\/span>`: 匹配 `<p class="win">` 开始标签,直到第一个 `span` 标签的 `title` 属性,并捕获其值。
- `(?:.*?<span title="(.*?)" .*?<\/span>)?`: 匹配从第一个 `span` 标签结束到第二个 `span` 标签结束的内容,并捕获第二个 `span` 标签的 `title` 属性,整个部分是可选的。
- `?` 表示前面的非捕获组是可选的,可以出现 0 次或 1 次。
再对正则知识点细节性问:
(?: ... ) 表示一个非捕获组,不会保存匹配结果。
上面这个是什么意思,请再接合解释下面正则:
(?:.?<span title="(.?)" .*?</span>)?
ChatGPT回答:
好的,让我们详细解释一下 `(?: ... )` 和具体的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?`。
### 非捕获组 `(?: ... )`
- `(?: ... )` 是一个**非捕获组**,它的作用是将括号内的内容进行分组,但不保存这个组的匹配结果。这和普通的捕获组 `(...)` 不同,普通捕获组会保存匹配结果供后续引用,而非捕获组则不会保存。
- 非捕获组主要用于在需要进行分组但不希望保存匹配结果的情况下,这样可以节省内存,并且在使用正则表达式时不需要担心额外的捕获组会影响捕获组编号。
### 具体的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?`
#### 分解解释
1. **`(?: ... )`**:
- 这是一个非捕获组,括号内的内容会被当作一个整体来处理,但不会保存匹配结果。
2. **`.*?`**:
- 非贪婪模式匹配任意字符,尽量少的字符,直到遇到 `<span title=`。
- 这里匹配的是从 `<p class="win">` 到第一个 `<span title="...">` 之间的任意字符,包括换行符等。
3. **`<span title="(.*?)"`**:
- 匹配 `<span title="` 字符串,并捕获 `title` 属性的值。
- `(.*?)` 是一个非贪婪模式的捕获组,匹配 `title` 属性的内容。例如,`南风`。
4. **` .*?<\/span>`**:
- 非贪婪模式匹配任意字符,直到遇到 `</span>`。
- 这里匹配的是从 `title` 属性结束到 `</span>` 标签结束的所有字符。
5. **`(?: ... )?`**:
- 问号 `?` 表示前面的非捕获组是可选的,可以匹配 0 次或 1 次。
- 整个非捕获组 `(?: ... )` 是可选的,意味着可以有一个额外的 `span` 标签,或者没有。
#### 具体示例
以你的 HTML 代码为例:
```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i><3级</i>
</p>
```
匹配过程如下:
1. **第一部分** `<p class="win">.*?<span title="(.*?)" .*?<\/span>`:
- 匹配 `<p class="win">` 开始标签。
- `.*?` 非贪婪模式匹配任意字符,直到遇到第一个 `<span title=`。
- 匹配并捕获第一个 `span` 标签的 `title` 属性值 `南风`。
- 匹配 `</span>` 标签。
2. **第二部分** `(?:.*?<span title="(.*?)" .*?<\/span>)?`:
- `(?: ... )` 非捕获组,将内容作为整体处理。
- `.*?` 非贪婪模式匹配任意字符,直到遇到第二个 `<span title=`。
- 匹配并捕获第二个 `span` 标签的 `title` 属性值 `南风`。
- 匹配 `</span>` 标签。
- `?` 表示非捕获组是可选的,可以匹配 0 次或 1 次。
### 完整正则表达式的匹配过程
完整的正则表达式 `(?:.*?<span title="(.*?)" .*?<\/span>)?` 在匹配过程中的作用如下:
1. 从 `<p class="win">` 开始,匹配任意字符直到第一个 `<span title=`。
2. 捕获第一个 `span` 标签的 `title` 属性值。
3. 匹配 `</span>` 标签。
4. 非捕获组 `(?: ... )?` 的内容(可选):
- 匹配任意字符直到第二个 `<span title=`。
- 捕获第二个 `span` 标签的 `title` 属性值。
- 匹配 `</span>` 标签。
这段正则表达式会成功匹配以下内容,并分别捕获两个 `span` 标签的 `title` 属性值:
```html
<p class="win">
<em>
<span title="南风" class="S"></span>
<span title="南风" class="S"></span>
</em>
<i><3级</i>
</p>
```
结果是捕获两个 `span` 标签的 `title` 属性值 `南风` 和 `南风`。如果只有一个 `span` 标签,那么也能成功匹配,只会捕获第一个 `title` 属性值。
cat haiyangguan.sh
#!/bin/bash
# 获取当前时间戳(毫秒)
current_timestamp=$(($(date +%s%N)/1000000))
# 定义curl命令,使用当前时间戳替换原时间戳
curl_command="curl -H 'Host: ocean.shuziwenlv.com' -H 'content-type: application/json; charset=UTF-8' -H 'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.49(0x18003133) NetType/WIFI Language/zh_CN' -H 'Referer: https://servicewechat.com/wx49d59947448fc8a7/40/page-frame.html' --data-binary '{\"header\":{\"userNo\":\"\",\"serviceName\":\"CreateOrder\",\"version\":\"v1.0\",\"token\":\"\",\"cacheTime\":0,\"timestamp\":$current_timestamp},\"body\":\"JTdCJTIyZGF0ZSUyMiUzQSUyMjIwMjQtMDYtMDklMjIlMkMlMjJzdHJhdGVneUlkJTIyJTNBJTIyMTAwMDAwMDI1NyUyMiUyQyUyMnByb2plY3RObyUyMiUzQSUyMjEwMDAwMDA2NjYlMjIlMkMlMjJzdWJQcm9qZWN0Tm8lMjIlM0ExMDAwMDAwNjY2JTJDJTIyb3BlbklkJTIyJTNBJTIyb0NhdTU2NGYzTDluYjRIdGlPN1RfOUd6WGExNCUyMiUyQyUyMmFwcE5vJTIyJTNBJTIyMTAlMjIlMkMlMjJ0aWNrZXRMaXN0JTIyJTNBJTVCJTdCJTIyc3R5bGVObyUyMiUzQSUyMjEwMDAwMDEwMDAlMjIlMkMlMjJjb3VudCUyMiUzQTMlMkMlMjJ0b3VyaXN0TGlzdCUyMiUzQSU1QiU3QiUyMm5hbWUlMjIlM0ElMjIlRTUlQkQlQTYlRTYlOTklOTMlRTglOEIlQjklMjIlMkMlMjJtb2JpbGVObyUyMiUzQSUyMjE4NjEwODE1NzA4JTIyJTJDJTIyY2FyZFR5cGUlMjIlM0ElMjIxMCUyMiUyQyUyMmNhcmRObyUyMiUzQSUyMjE1MjMyNjE5ODUwMTE0MzU4OSUyMiUyQyUyMnNlYXRObyUyMiUzQSUyMiUyMiUyQyUyMnNlYXRtYXBObyUyMiUzQSUyMiUyMiUyQyUyMnRpZHglMjIlM0ExJTdEJTJDJTdCJTIybmFtZSUyMiUzQSUyMiVFNSU5MCU5MSVFNCVCOCU5QyUyMiUyQyUyMm1vYmlsZU5vJTIyJTNBJTIyMTg4MTA3NjUxMDglMjIlMkMlMjJjYXJkVHlwZSUyMiUzQSUyMjEwJTIyJTJDJTIyY2FyZE5vJTIyJTNBJTIyNTAwMjMwMTk4NTEwMDI3MTFYJTIyJTJDJTIyc2VhdE5vJTIyJTNBJTIyJTIyJTJDJTIyc2VhdG1hcE5vJTIyJTNBJTIyJTIyJTJDJTIydGlkeCUyMiUzQTIlN0QlMkMlN0IlMjJuYW1lJTIyJTNBJTIyJUU1JTkwJTkxJUU2JUI1JUE5JUU5JUI4JUEzJTIyJTJDJTIybW9iaWxlTm8lMjIlM0ElMjIxMzI2MDE2ODA4NiUyMiUyQyUyMmNhcmRUeXBlJTIyJTNBJTIyMTAlMjIlMkMlMjJjYXJkTm8lMjIlM0ElMjIxMTAxMTIyMDE5MTAyNDQ2MTklMjIlMkMlMjJzZWF0Tm8lMjIlM0ElMjIlMjIlMkMlMjJzZWF0bWFwTm8lMjIlM0ElMjIlMjIlMkMlMjJ0aWR4JTIyJTNBMyU3RCU1RCU3RCU1RCU3RA==\"}' --compressed 'https://ocean.shuziwenlv.com/MiniProgram/ProcessRequest'"
# 执行curl命令
eval $curl_command
#cat run_haiyangguan.sh
^M 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0^M100 1726 100 244 100 1482 829 5039 --:--:-- --:--:-- --:--:-- 5058
{"header":{"status":999,"timecost":19,"msg":"剩余库存不足","serviceName":"CreateOrder","serviceUnavailable":false},"body":""} % Total % Received % Xferd Average Speed Tim
e Time Time Current
Dload Upload Total Spent Left Speed
^M 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0^M100 1482 0 0 100 1482 0 1225 0:00:01 0:00:01 --:--:-- 1225^M100 1798 100
316 100 1482 156 734 0:00:02 0:00:02 --:--:-- 734^M100 1798 100 316 100 1482 156 734 0:00:02 0:00:02 --:--:-- 734
{"header":{"status":200,"timecost":1556,"msg":"操作成功","serviceName":"CreateOrder","serviceUnavailable":false},"body":"JTdiJTIybGF1bmNoJTIyJTNhZmFsc2UlMmMlMjJvcmRlck5vJTIyJTNh
JTIyMjAwMDUzNTg5OCUyMiU3ZA=="} % Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
^M 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0^M100 1751 100 269 100 1482 535 2948 --:--:-- --:--:-- --:--:-- 2946^M100 1751 100
269 100 1482 534 2946 --:--:-- --:--:-- --:--:-- 2946
{"header":{"status":999,"timecost":27,"msg":"证件:152**3589请勿重复预定!","serviceName":"CreateOrder","serviceUnavailable":false},"body":""} % Total % Received % Xferd Aver
age Speed Time Time Time Current
Dload Upload Total Spent Left Speed
^M 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0^M100 1751 100 269 100 1482 928 5115 --:--:-- --:--:-- --:--:-- 5128
{"header":{"status":999,"timecost":18,"msg":"证件:152**3589请勿重复预定!","serviceName":"CreateOrder","serviceUnavailable":false},"body":""} % Total % Received % Xferd Aver
age Speed Time Time Time Current
Dload Upload Total Spent Left Speed
^M 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0^M100 1751 100 269 100 1482 547 3015 --:--:-- --:--:-- --:--:-- 3018
{"header":{"status":999,"timecost":17,"msg":"证件:152**3589请勿重复预定!","serviceName":"CreateOrder","serviceUnavailable":false},"body":""} % Total % Received % Xferd Aver
二次升级:
curl -H 'Host: ocean.shuziwenlv.com' -H 'content-type: application/json; charset=UTF-8' -H 'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.49(0x18003133) NetType/WIFI Language/zh_CN' -H 'Referer: https://servicewechat.com/wx49d59947448fc8a7/40/page-frame.html' --data-binary '{"header":{"userNo":"","serviceName":"CreateOrder","version":"v1.0","token":"","cacheTime":0,"timestamp":1724805497445},"body":"JTdCJTIyZGF0ZSUyMiUzQSUyMjIwMjQtMDktMDElMjIlMkMlMjJzdHJhdGVneUlkJTIyJTNBJTIyMTAwMDAwMDI1NyUyMiUyQyUyMnByb2plY3RObyUyMiUzQSUyMjEwMDAwMDA2NjYlMjIlMkMlMjJzdWJQcm9qZWN0Tm8lMjIlM0ExMDAwMDAwNjY2JTJDJTIyb3BlbklkJTIyJTNBJTIyb0NhdTU2NGYzTDluYjRIdGlPN1RfOUd6WGExNCUyMiUyQyUyMmFwcE5vJTIyJTNBJTIyMTAlMjIlMkMlMjJ0aWNrZXRMaXN0JTIyJTNBJTVCJTdCJTIyc3R5bGVObyUyMiUzQSUyMjEwMDAwMDEwMDAlMjIlMkMlMjJjb3VudCUyMiUzQTQlMkMlMjJ0b3VyaXN0TGlzdCUyMiUzQSU1QiU3QiUyMm5hbWUlMjIlM0ElMjIlRTUlQkQlQTYlRTYlOTklOTMlRTglOEIlQjklMjIlMkMlMjJtb2JpbGVObyUyMiUzQSUyMjE4NjEwODE1NzA4JTIyJTJDJTIyY2FyZFR5cGUlMjIlM0ElMjIxMCUyMiUyQyUyMmNhcmRObyUyMiUzQSUyMjE1MjMyNjE5ODUwMTE0MzU4OSUyMiUyQyUyMnNlYXRObyUyMiUzQSUyMiUyMiUyQyUyMnNlYXRtYXBObyUyMiUzQSUyMiUyMiUyQyUyMnRpZHglMjIlM0ExJTdEJTJDJTdCJTIybmFtZSUyMiUzQSUyMiVFNSU5MCU5MSVFNCVCOCU5QyUyMiUyQyUyMm1vYmlsZU5vJTIyJTNBJTIyMTg4MTA3NjUxMDglMjIlMkMlMjJjYXJkVHlwZSUyMiUzQSUyMjEwJTIyJTJDJTIyY2FyZE5vJTIyJTNBJTIyNTAwMjMwMTk4NTEwMDI3MTFYJTIyJTJDJTIyc2VhdE5vJTIyJTNBJTIyJTIyJTJDJTIyc2VhdG1hcE5vJTIyJTNBJTIyJTIyJTJDJTIydGlkeCUyMiUzQTIlN0QlMkMlN0IlMjJuYW1lJTIyJTNBJTIyJUU1JTkwJTkxJUU2JUI1JUE5JUU5JUI4JUEzJTIyJTJDJTIybW9iaWxlTm8lMjIlM0ElMjIxMzI2MDE2ODA4NiUyMiUyQyUyMmNhcmRUeXBlJTIyJTNBJTIyMTAlMjIlMkMlMjJjYXJkTm8lMjIlM0ElMjIxMTAxMTIyMDE5MTAyNDQ2MTklMjIlMkMlMjJzZWF0Tm8lMjIlM0ElMjIlMjIlMkMlMjJzZWF0bWFwTm8lMjIlM0ElMjIlMjIlMkMlMjJ0aWR4JTIyJTNBMyU3RCUyQyU3QiUyMm5hbWUlMjIlM0ElMjIlRTUlOTAlOTElRTUlQUUlODclMjIlMkMlMjJtb2JpbGVObyUyMiUzQSUyMjE2NjIzMTYwOTAyJTIyJTJDJTIyY2FyZFR5cGUlMjIlM0ElMjIxMCUyMiUyQyUyMmNhcmRObyUyMiUzQSUyMjUwMDIzMDIwMDQwOTAyNzExNSUyMiUyQyUyMnNlYXRObyUyMiUzQSUyMiUyMiUyQyUyMnNlYXRtYXBObyUyMiUzQSUyMiUyMiUyQyUyMnRpZHglMjIlM0E0JTdEJTVEJTdEJTVEJTdECg=="}' --compressed https://ocean.shuziwenlv.com/MiniProgram/ProcessRequest
{"header":{"status":301,"timecost":0,"msg":"请求header传入的方法名称不正确","serviceName":"CreateOrder","serviceUnavailable":false},"body":""}
cat haiyangguan.sh
#!/bin/bash
# 获取当前时间戳(毫秒)
current_timestamp=$(($(date +%s%N)/1000000))
# 定义curl命令,使用当前时间戳替换原时间戳
curl_command="curl -H 'Host: ocean.shuziwenlv.com' -H 'content-type: application/json; charset=UTF-8' -H 'User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_5_1 like Mac OS X) AppleWebKit/605
.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.49(0x18003133) NetType/WIFI Language/zh_CN' -H 'Referer: https://servicewechat.com/wx49d59947448fc8a7/40/page-frame.html' --data-bi
nary '{\"header\":{\"userNo\":\"\",\"serviceName\":\"CreateOrder\",\"version\":\"v1.0\",\"token\":\"\",\"cacheTime\":0,\"timestamp\":$current_timestamp},\"body\":\"JTdCJTIyZGF0ZSUyMiUzQSUyMj
IwMjQtMDktMDElMjIlMkMlMjJzdHJhdGVneUlkJTIyJTNBJTIyMTAwMDAwMDI1NyUyMiUyQyUyMnByb2plY3RObyUyMiUzQSUyMjEwMDAwMDA2NjYlMjIlMkMlMjJzdWJQcm9qZWN0Tm8lMjIlM0ExMDAwMDAwNjY2JTJDJTIyb3BlbklkJTIyJTNBJTIy
b0NhdTU2NGYzTDluYjRIdGlPN1RfOUd6WGExNCUyMiUyQyUyMmFwcE5vJTIyJTNBJTIyMTAlMjIlMkMlMjJ0aWNrZXRMaXN0JTIyJTNBJTVCJTdCJTIyc3R5bGVObyUyMiUzQSUyMjEwMDAwMDEwMDAlMjIlMkMlMjJjb3VudCUyMiUzQTQlMkMlMjJ0b3
VyaXN0TGlzdCUyMiUzQSU1QiU3QiUyMm5hbWUlMjIlM0ElMjIlRTUlQkQlQTYlRTYlOTklOTMlRTglOEIlQjklMjIlMkMlMjJtb2JpbGVObyUyMiUzQSUyMjE4NjEwODE1NzA4JTIyJTJDJTIyY2FyZFR5cGUlMjIlM0ElMjIxMCUyMiUyQyUyMmNhcmRO
byUyMiUzQSUyMjE1MjMyNjE5ODUwMTE0MzU4OSUyMiUyQyUyMnNlYXRObyUyMiUzQSUyMiUyMiUyQyUyMnNlYXRtYXBObyUyMiUzQSUyMiUyMiUyQyUyMnRpZHglMjIlM0ExJTdEJTJDJTdCJTIybmFtZSUyMiUzQSUyMiVFNSU5MCU5MSVFNCVCOCU5Qy
UyMiUyQyUyMm1vYmlsZU5vJTIyJTNBJTIyMTg4MTA3NjUxMDglMjIlMkMlMjJjYXJkVHlwZSUyMiUzQSUyMjEwJTIyJTJDJTIyY2FyZE5vJTIyJTNBJTIyNTAwMjMwMTk4NTEwMDI3MTFYJTIyJTJDJTIyc2VhdE5vJTIyJTNBJTIyJTIyJTJDJTIyc2Vh
dG1hcE5vJTIyJTNBJTIyJTIyJTJDJTIydGlkeCUyMiUzQTIlN0QlMkMlN0IlMjJuYW1lJTIyJTNBJTIyJUU1JTkwJTkxJUU2JUI1JUE5JUU5JUI4JUEzJTIyJTJDJTIybW9iaWxlTm8lMjIlM0ElMjIxMzI2MDE2ODA4NiUyMiUyQyUyMmNhcmRUeXBlJT
IyJTNBJTIyMTAlMjIlMkMlMjJjYXJkTm8lMjIlM0ElMjIxMTAxMTIyMDE5MTAyNDQ2MTklMjIlMkMlMjJzZWF0Tm8lMjIlM0ElMjIlMjIlMkMlMjJzZWF0bWFwTm8lMjIlM0ElMjIlMjIlMkMlMjJ0aWR4JTIyJTNBMyU3RCUyQyU3QiUyMm5hbWUlMjIl
M0ElMjIlRTUlOTAlOTElRTUlQUUlODclMjIlMkMlMjJtb2JpbGVObyUyMiUzQSUyMjE2NjIzMTYwOTAyJTIyJTJDJTIyY2FyZFR5cGUlMjIlM0ElMjIxMCUyMiUyQyUyMmNhcmRObyUyMiUzQSUyMjUwMDIzMDIwMDQwOTAyNzExNSUyMiUyQyUyMnNlYX
RObyUyMiUzQSUyMiUyMiUyQyUyMnNlYXRtYXBObyUyMiUzQSUyMiUyMiUyQyUyMnRpZHglMjIlM0E0JTdEJTVEJTdEJTVEJTdECg==\"}' --compressed 'https://ocean.shuziwenlv.com/MiniProgram/ProcessRequest'"
# 执行curl命令
eval $curl_command
URLEncode后再Base64编码传输到服务器端的研究:
上面将URL的内容还成成可读的内容,专业术语叫什么来着。
同样,反过来将可读内容urlencode成传输内容的术语叫啥?
将URL编码的内容还原成可读内容的过程称为URL解码(URL Decoding)。相反,将可读内容转换为URL编码格式的过程称为URL编码(URL Encoding)。
以下是使用PHP和Go语言进行URL编码(URL Encoding)和解码(URL Decoding)的示例代码。
### 1. PHP
#### URL解码(URL Decoding)
```php
<?php
// URL编码后的JSON
$urlEncoded = "%7B%22date%22%3A%222024-09-01%22%2C%22strategyId%22%3A%221000000257%22%2C%22projectNo%22%3A%221000000666%22%2C%22subProjectNo%22%3A1000000666%2C%22openId%22%3A%22oCau564f3L9nb4HtiO7T_9GzXa14%22%2C%22appNo%22%3A%2210%22%2C%22ticketList%22%3A%5B%7B%22styleNo%22%3A%221000001000%22%2C%22count%22%3A4%2C%22touristList%22%3A%5B%7B%22name%22%3A%22%E5%BD%A6%E6%99%93%E8%8B%B9%22%2C%22mobileNo%22%3A%2218610815708%22%2C%22cardType%22%3A%2210%22%2C%22cardNo%22%3A%22152326198501143589%22%2C%22seatNo%22%3A%22%22%2C%22seatmapNo%22%3A%22%22%2C%22tidx%22%3A1%7D%2C%7B%22name%22%3A%22%E5%90%91%E4%B8%9C%22%2C%22mobileNo%22%3A%2218810765108%22%2C%22cardType%22%3A%2210%22%2C%22cardNo%22%3A%2250023019851002711X%22%2C%22seatNo%22%3A%22%22%2C%22seatmapNo%22%3A%22%22%2C%22tidx%22%3A2%7D%2C%7B%22name%22%3A%22%E5%90%91%E6%B5%A9%E9%B8%A3%22%2C%22mobileNo%22%3A%2213260168086%22%2C%22cardType%22%3A%2210%22%2C%22cardNo%22%3A%22110112201910244619%22%2C%22seatNo%22%3A%22%22%2C%22seatmapNo%22%3A%22%22%2C%22tidx%22%3A3%7D%2C%7B%22name%22%3A%22%E5%90%91%E5%AE%87%22%2C%22mobileNo%22%3A%2216623160902%22%2C%22cardType%22%3A%2210%22%2C%22cardNo%22%3A%22500230200409027115%22%2C%22seatNo%22%3A%22%22%2C%22seatmapNo%22%3A%22%22%2C%22tidx%22%3A4%7D%5D%7D%5D%7D";
// 进行URL解码
$decoded = urldecode($urlEncoded);
// 打印解码后的JSON
echo $decoded;
?>
```
#### URL编码(URL Encoding)
```php
<?php
// 明文JSON内容
$json = '{
"date": "2024-09-01",
"strategyId": "1000000257",
"projectNo": "1000000666",
"subProjectNo": 1000000666,
"openId": "oCau564f3L9nb4HtiO7T_9GzXa14",
"appNo": "10",
"ticketList": [
{
"styleNo": "1000001000",
"count": 4,
"touristList": [
{
"name": "彦晓苹",
"mobileNo": "18610815708",
"cardType": "10",
"cardNo": "152326198501143589",
"seatNo": "",
"seatmapNo": "",
"tidx": 1
},
{
"name": "向东",
"mobileNo": "18810765108",
"cardType": "10",
"cardNo": "50023019851002711X",
"seatNo": "",
"seatmapNo": "",
"tidx": 2
},
{
"name": "向浩鸣",
"mobileNo": "13260168086",
"cardType": "10",
"cardNo": "110112201910244619",
"seatNo": "",
"seatmapNo": "",
"tidx": 3
},
{
"name": "向宇",
"mobileNo": "16623160902",
"cardType": "10",
"cardNo": "500230200409027115",
"seatNo": "",
"seatmapNo": "",
"tidx": 4
}
]
}
]
}';
// 进行URL编码
$encoded = urlencode($json);
// 打印编码后的URL格式内容
echo $encoded;
?>
```
### 2. Go
#### URL解码(URL Decoding)
```go
package main
import (
"fmt"
"net/url"
)
func main() {
// URL编码后的JSON
urlEncoded := "%7B%22date%22%3A%222024-09-01%22%2C%22strategyId%22%3A%221000000257%22%2C%22projectNo%22%3A%221000000666%22%2C%22subProjectNo%22%3A1000000666%2C%22openId%22%3A%22oCau564f3L9nb4HtiO7T_9GzXa14%22%2C%22appNo%22%3A%2210%22%2C%22ticketList%22%3A%5B%7B%22styleNo%22%3A%221000001000%22%2C%22count%22%3A4%2C%22touristList%22%3A%5B%7B%22name%22%3A%22%E5%BD%A6%E6%99%93%E8%8B%B9%22%2C%22mobileNo%22%3A%2218610815708%22%2C%22cardType%22%3A%2210%22%2C%22cardNo%22%3A%22152326198501143589%22%2C%22seatNo%22%3A%22%22%2C%22seatmapNo%22%3A%22%22%2C%22tidx%22%3A1%7D%2C%7B%22name%22%3A%22%E5%90%91%E4%B8%9C%22%2C%22mobileNo%22%3A%2218810765108%22%2C%22cardType%22%3A%2210%22%2C%22cardNo%22%3A%2250023019851002711X%22%2C%22seatNo%22%3A%22%22%2C%22seatmapNo%22%3A%22%22%2C%22tidx%22%3A2%7D%2C%7B%22name%22%3A%22%E5%90%91%E6%B5%A9%E9%B8%A3%22%2C%22mobileNo%22%3A%2213260168086%22%2C%22cardType%22%3A%2210%22%2C%22cardNo%22%3A%22110112201910244619%22%2C%22seatNo%22%3A%22%22%2C%22seatmapNo%22%3A%22%22%2C%22tidx%22%3A3%7D%2C%7B%22name%22%3A%22%E5%90%91%E5%AE%87%22%2C%22mobileNo%22%3A%2216623160902%22%2C%22cardType%22%3A%2210%22%2C%22cardNo%22%3A%22500230200409027115%22%2C%22
用PHP和GO语言,分别将上面的JSON进行解码和编码,学习一下,谢谢。
问题:
为何要进行URL编码?以及它对哪些字符作了转化,转化的目的是什么?
回答:
URL编码(URL Encoding)的主要目的是确保在URL中传输数据时,字符不会被错误地解释或丢失。URL中的某些字符具有特殊的意义(如`?`, `&`, `/`, `=`等),它们被用来分隔URL的不同部分(例如查询参数),因此如果这些字符出现在数据中(如查询字符串、路径参数或表单数据),它们可能会导致URL解析错误。URL编码通过将这些字符转换为特定的编码形式来避免此类问题。
### 为什么进行URL编码?
1. **避免歧义**:URL中的特殊字符(如`?`, `&`, `/`等)可能会与实际数据中的字符混淆,导致URL解析错误。URL编码能够将这些特殊字符转换为不具有特殊意义的形式,以避免歧义。
2. **确保兼容性**:某些字符在URL中是非法的(如空格、引号、尖括号等),或者某些字符在不同的浏览器和服务器之间的处理可能不同。通过URL编码,可以确保这些字符在所有情况下都能正确传输和解释。
3. **编码非ASCII字符**:URL通常只能使用ASCII字符集,因此需要将非ASCII字符(如中文、日文等)编码为ASCII字符,以确保它们能在URL中被正确传输。
### URL编码对哪些字符进行了转化?
在URL编码中,某些字符会被转换为以`%`开头的三字符表示形式,其中后两字符是该字符的ASCII值的十六进制表示。以下是常见的字符及其URL编码后的形式:
- **空格(Space)**: `%20`
- **双引号(")**: `%22`
- **#号**: `%23`
- **$符号**: `%24`
- **%符号**: `%25`
- **&符号**: `%26`
- **'符号**: `%27`
- **(符号**: `%28`
- **)符号**: `%29`
- **+号**: `%2B`
- **,符号**: `%2C`
- **/符号**: `%2F`
- **:符号**: `%3A`
- **;符号**: `%3B`
- **<符号**: `%3C`
- **=符号**: `%3D`
- **>符号**: `%3E`
- **?符号**: `%3F`
- **@符号**: `%40`
- **[符号**: `%5B`
- **\符号**: `%5C`
- **]符号**: `%5D`
- **`符号**: `%60`
- **{符号**: `%7B`
- **|符号**: `%7C`
- **}符号**: `%7D`
### 转化的目的是什么?
1. **安全传输**:防止在URL中传输数据时,因特殊字符导致数据的误解析或错误解释。
2. **合法性**:保证URL符合标准规范,避免因非法字符导致的解析问题。
3. **一致性**:确保不同系统、浏览器、服务器对URL的处理和理解是一致的。
URL编码是网络通信中不可或缺的一部分,确保了数据在不同环境之间的安全、可靠传输。
问题:UrlEncode主要是放在浏览器的GET参数中传输吧,POST中就不需要进行UrlEncode吧? 为什么经过了urlencode的数据放在curl中的--data-binary还要再进行一次base64转码呢?
答案:
URL编码主要用于确保数据在URL中传输时不被错误解释。对于GET请求,参数通常直接附在URL后面,因此必须进行URL编码以处理特殊字符。但对于POST请求,数据通常在请求体中传输,这种情况下,是否需要URL编码取决于数据格式和传输方式。
### 关于URL编码在POST请求中的使用:
1. **表单提交(`application/x-www-form-urlencoded`)**:
- 默认情况下,表单数据会自动使用`application/x-www-form-urlencoded`的MIME类型,这意味着表单字段的值将被URL编码。
- 例如,`name=John Doe&age=25`会变成`name=John%20Doe&age=25`。
2. **JSON或其他数据格式(`application/json`)**:
- 如果POST请求以JSON或其他格式发送数据(如`application/json`),通常不需要URL编码,因为数据是以原始格式直接放在请求体中。
- 例如,JSON数据`{"name": "John Doe", "age": 25}`不会被编码,而是直接作为请求体的一部分发送。
### 为什么在`curl`中使用`--data-binary`的同时还要进行Base64编码?
Base64编码是一种将二进制数据转换为ASCII字符的编码方式。以下是一些场景中使用Base64编码的原因:
1. **处理二进制数据**:
- `--data-binary`在`curl`中用于直接传输原始数据,不进行任何处理。对于某些API或服务器而言,如果数据中包含不可见字符或特殊字符(例如图像、加密数据、二进制文件),可能会导致解析问题。因此,使用Base64编码可以确保这些数据在传输过程中不会被误解或损坏。
2. **兼容性**:
- 某些服务器或应用可能要求或更容易处理Base64编码的数据。Base64编码将数据转换为只包含字母、数字及少量符号的ASCII字符,从而避免了特殊字符导致的解析问题。
3. **安全性**:
- 在某些情况下,数据经过URL编码后仍然可能包含对服务器或API具有特殊意义的字符。这些字符可能会触发某些意外行为或安全风险。通过额外的Base64编码,可以增加数据的可靠性和安全性,防止这些问题的发生。
### 综合考虑:
- 在GET请求中,URL编码是必须的。
- 在POST请求中,URL编码取决于内容类型和数据格式。
- Base64编码是为了解决特殊字符、二进制数据传输问题,以及提高数据的安全性和兼容性。
因此,数据即使经过URL编码,在传输中可能仍然需要Base64编码以确保数据的完整性和安全性。
[实践OK]CentOS7下面screen is very slow when it opens new window。
Unix/LinuxC技术 jackxiang 2024-4-17 17:41
除开strace外还有一款性能分析工具:
sudo yum install perf
perf record -F max -a -g -- screen -ls
perf record -F max -a -g -- screen -ls
info: Using a maximum frequency rate of 11,000 Hz
There is a screen on:
16882.abc (Detached)
1 Socket in /var/run/screen/S-root.
[ perf record: Woken up 14 times to write data ]
[ perf record: Captured and wrote 4.143 MB perf.data (34888 samples) ]
可在不同设备上读取的perf.data 文件. 您可以使用 perf 工具将性能数据记录到 perf.data 文件中,以便在不同的设备上分析。
echo Soft: $(ulimit -Sn)
echo Hard: $(ulimit -Hn)
Soft: 1024000
Hard: 1024000
立即生效的解决办法(重启配置失效):
ulimit -Sn 10000
ulimit -Hn 50000
重启后永久生效实操:
要在 CentOS 7 上永久设置 `ulimit`,您需要修改 `/etc/security/limits.conf` 文件。这个文件允许您设置系统级别的资源限制。您可以按照以下步骤进行修改:
1. 打开终端并以 root 用户身份登录。
2. 使用文本编辑器(如 `vi` 或 `nano`)打开 `/etc/security/limits.conf` 文件:
```bash
sudo vi /etc/security/limits.conf
```
3. 在文件末尾添加以下行:
```
* soft nofile 10000
* hard nofile 50000
```
这里,`soft` 代表软限制,`hard` 代表硬限制。软限制是系统允许的最大值,超过此限制时会发出警告。硬限制是系统允许的绝对最大值,超过此限制时会导致操作失败。通常情况下,软限制和硬限制是相同的。
4. 保存并关闭文件。在 vi 中,您可以按 `Esc` 键输入 `:wq` 然后按 `Enter` 保存并退出。
5. 重新启动系统或重新登录以使更改生效。
现在 `ulimit` 的设置应该在系统重新启动后生效,并且将永久保持。
fix: Normalize RLIMIT_NOFILE (LimitNOFILE) to sensible defaults:
https://github.com/moby/moby/pull/45534
gnu screen run extremely slow when using 'root' user in container since docker 23.0.1 #45380:
https://github.com/moby/moby/issues/45380
=============================================================
火焰图
on-cpu火焰图可以用于分析cpu是被哪些线程、哪些函数占用的,可以方便的找到热点代码便于后续分析优化。下面我们介绍下火焰图的生成和使用方法。
使用方法
准备FlameGraph工具。
git clone https://github.com/brendangregg/FlameGraph.git
用perf record采集CPU信息。
perf record -e cpu-clock -g ./perf_test
Ctrl+c结束执行后,在当前目录下会生成采样数据perf.data。
用perf script工具对perf.data进行解析。
perf script -i perf.data &> perf.unfold
将perf.unfold中的符号进行折叠。
./stackcollapse-perf.pl perf.unfold &> perf.folded
最后生成svg图。
./flamegraph.pl perf.folded > perf.svg
perf.svg 用浏览器就可以打开
来自:https://github.com/moby/moby/pull/45534
sudo yum install perf
perf record -F max -a -g -- screen -ls
perf record -F max -a -g -- screen -ls
info: Using a maximum frequency rate of 11,000 Hz
There is a screen on:
16882.abc (Detached)
1 Socket in /var/run/screen/S-root.
[ perf record: Woken up 14 times to write data ]
[ perf record: Captured and wrote 4.143 MB perf.data (34888 samples) ]
可在不同设备上读取的perf.data 文件. 您可以使用 perf 工具将性能数据记录到 perf.data 文件中,以便在不同的设备上分析。
echo Soft: $(ulimit -Sn)
echo Hard: $(ulimit -Hn)
Soft: 1024000
Hard: 1024000
立即生效的解决办法(重启配置失效):
ulimit -Sn 10000
ulimit -Hn 50000
重启后永久生效实操:
要在 CentOS 7 上永久设置 `ulimit`,您需要修改 `/etc/security/limits.conf` 文件。这个文件允许您设置系统级别的资源限制。您可以按照以下步骤进行修改:
1. 打开终端并以 root 用户身份登录。
2. 使用文本编辑器(如 `vi` 或 `nano`)打开 `/etc/security/limits.conf` 文件:
```bash
sudo vi /etc/security/limits.conf
```
3. 在文件末尾添加以下行:
```
* soft nofile 10000
* hard nofile 50000
```
这里,`soft` 代表软限制,`hard` 代表硬限制。软限制是系统允许的最大值,超过此限制时会发出警告。硬限制是系统允许的绝对最大值,超过此限制时会导致操作失败。通常情况下,软限制和硬限制是相同的。
4. 保存并关闭文件。在 vi 中,您可以按 `Esc` 键输入 `:wq` 然后按 `Enter` 保存并退出。
5. 重新启动系统或重新登录以使更改生效。
现在 `ulimit` 的设置应该在系统重新启动后生效,并且将永久保持。
fix: Normalize RLIMIT_NOFILE (LimitNOFILE) to sensible defaults:
https://github.com/moby/moby/pull/45534
gnu screen run extremely slow when using 'root' user in container since docker 23.0.1 #45380:
https://github.com/moby/moby/issues/45380
=============================================================
火焰图
on-cpu火焰图可以用于分析cpu是被哪些线程、哪些函数占用的,可以方便的找到热点代码便于后续分析优化。下面我们介绍下火焰图的生成和使用方法。
使用方法
准备FlameGraph工具。
git clone https://github.com/brendangregg/FlameGraph.git
用perf record采集CPU信息。
perf record -e cpu-clock -g ./perf_test
Ctrl+c结束执行后,在当前目录下会生成采样数据perf.data。
用perf script工具对perf.data进行解析。
perf script -i perf.data &> perf.unfold
将perf.unfold中的符号进行折叠。
./stackcollapse-perf.pl perf.unfold &> perf.folded
最后生成svg图。
./flamegraph.pl perf.folded > perf.svg
perf.svg 用浏览器就可以打开
来自:https://github.com/moby/moby/pull/45534
对于:[实践OK]串口学习之上位机查询模式,下位机arduino采用中断方式读取上位机的指令并crc验证其准确性后读取Tsic506的温度值,再通过CRC校验后经串口发送到上位机。https://jackxiang.com/post/12322/ 这部分串口问题的读取采用select模型的细节分析。
优秀,但最终有一个点仍然想向您探讨一下,上面您给修复后的代码片段中这一外逻辑:
} else if (num_bytes == 0) {
// 串口关闭或无数据可读
perror("Serial port closed or no data available");
exit(EXIT_FAILURE);
} else {
// 读取出错
perror("Error reading from serial port");
exit(EXIT_FAILURE);
}
上面代码子片段中,} else if (num_bytes == 0) {,是在select模型中通知有数据可以读到,我理解至少有一个byte及以上不会是0个byte,它什么样的情况下会出现:num_bytes == 0,再就是这种情况是串口硬件问题还是软件问题,是否可以了也给它一个修复 的机会呢?和select 模型一样:
if (consecutive_failures >= 5) {
// 连续失败超过5次,发送错误信息到服务器
send_error_to_server();
// 退出程序
exit(EXIT_FAILURE);
}
continue;
还是必须直接exit(EXIT_FAILURE);更为妥当呢?
二是:
} else {
// 读取出错
perror("Error reading from serial port");
exit(EXIT_FAILURE);
}
这部分如果串口读取出错,是否有什么标志位呢,直接放在else中就读取出错,是否有些武断了些,我想要是更谨慎编码,也请给出对应的最后else分支的读取出错的标志判断代码,让这部分代码更加健壮: } else {
// 读取出错
perror("Error reading from serial port");
exit(EXIT_FAILURE);
}
,我的两点要求问完了,您可以按条回复解答的同时,给出最终解决后并后的代码,十分感谢 。
回答:
关于您的第一个问题,`num_bytes == 0` 的情况通常表示从串口读取到的字节数为 0,即读取操作没有返回任何数据。这可能是因为串口关闭或者是没有数据可读。如果您认为这种情况是由于串口硬件问题而不是程序逻辑问题导致的,那么直接退出程序可能是更为合适的选择。因此,直接使用 `exit(EXIT_FAILURE)` 可能更为妥当。
对于第二个问题,如果串口读取出错,通常会返回 -1,并将 `errno` 设置为适当的错误代码。您可以根据 `errno` 的值来进一步判断错误的类型。因此,在 `else` 分支中,您可以添加一个针对 `errno` 的判断,以确定错误的具体原因,并根据需要采取相应的措施。
下面是针对您的两个问题的解答和相应的代码修正:
1. 对于 `num_bytes == 0` 的情况,我们直接退出程序,因为没有读取到任何数据,这可能是串口关闭或没有数据可读。
2. 对于读取出错的情况,我们可以根据 `errno` 的值判断错误的具体原因,并据此决定是否退出程序。这样可以使代码更加健壮,能够更好地处理可能的错误情况。
下面是修改后的代码片段:
```c
// 串口读取函数
void read_serial(Config *config) {
unsigned char buffer[BUFFER_SIZE];
ssize_t num_bytes;
int frame_start_detected = 0;
int recv_index = 0;
int consecutive_failures = 0; // 记录连续失败的次数
struct timeval tv;
fd_set rfds;
while (1) {
// 发送查询指令
send_query(config->serial_fd);
usleep(150000); // 等待150毫秒
// 设置超时时间为3秒
tv.tv_sec = 3;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(config->serial_fd, &rfds);
// 等待串口数据
int retval = select(config->serial_fd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
perror("Error in select");
exit(EXIT_FAILURE);
} else if (retval == 0) {
// 超时,增加连续失败计数器
consecutive_failures++;
if (consecutive_failures >= 5) {
// 连续失败超过5次,发送错误信息到服务器
send_error_to_server();
// 退出程序
exit(EXIT_FAILURE);
}
continue;
} else {
// 读取串口数据
num_bytes = read(config->serial_fd, buffer + recv_index, BUFFER_SIZE - recv_index);
if (num_bytes > 0) {
// 数据读取成功
consecutive_failures = 0; // 重置连续失败计数器
recv_index += num_bytes;
// 打印接收到的十六进制数据
printf("\n\nReceived data: ");
for(int i = 0;i < recv_index; ++i){
printf("%02X ",buffer[i]);
}
printf("\n");
// 判断帧头是否检测到
if (!frame_start_detected) {
for (int i = 0; i < recv_index; ++i) {
if (buffer[i] == 0xAA) {
frame_start_detected = 1;
// 移动已接收数据到缓冲区开头
memmove(buffer, buffer + i, recv_index - i);
recv_index -= i;
break;
}
}
}
// 判断是否接收到完整的数据包
if (frame_start_detected && recv_index >= EXPECTED_PACKET_LENGTH && buffer[recv_index - 1] == 0x55) {
// 去除帧头和帧尾后的数据长度
int data_length = recv_index - 2;
// 进行 CRC 校验,传递数据段给 verifyData 函数
if (verifyData(buffer + 1, data_length)) {
// 提取温度值并打印
float temperature = convertToTemperature(buffer + 1);
printf("Temperature: %.1f\n", temperature);
// 发送POST请求
send_temperature_via_post(temperature);
} else {
printf("CRC 校验失败\n");
}
// 清除已处理数据
recv_index = 0;
frame_start_detected = 0;
}
} else if (num_bytes == 0) {
// 串口关闭或无数据可读
perror("Serial port closed or no data available");
exit(EXIT_FAILURE);
} else {
// 读取出错
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 数据暂时不可用,继续下一次循环
continue
;
} else {
perror("Error reading from serial port");
exit(EXIT_FAILURE);
}
}
}
}
}
```
这样,当 `num_bytes == 0` 时,表示读取操作未返回任何数据,直接退出程序;当读取出错时,我们根据 `errno` 的值判断错误类型,如果是暂时不可用的错误(如 `EAGAIN` 或 `EWOULDBLOCK`),则继续下一次循环;否则,打印错误信息并退出程序。
细节追问:
很专业,您所回答的:
对于 num_bytes == 0 的情况,我们直接退出程序,因为没有读取到任何数据,这可能是串口关闭或没有数据可读。
这一句中,串口关闭可以理解,如下位机在select明明有数据上来了但这一瞬间串口关掉了,没有读取到任何数据。 但是除开这个特殊情况外还有其它情况吗?我理解明明select 侦测到有数据来了,那么这个数据肯定是num_bytes >= 1,怎么就出现明明就通知有数据就读取到0个数据的逻辑判断上来了呢?我有些不理解,难道这个select模型会出现误通知吗?
终极回答:
您提出了一个很好的问题。
在一般情况下,如果使用 `select` 函数或其他 I/O 多路复用机制,并且它返回了通知有数据可读,那么确实会期望从文件描述符中读取到至少一个字节的数据。这是因为 `select` 函数在通知有数据可读时,文件描述符已经准备好被读取了。
因此,对于 `num_bytes == 0` 的情况,通常不会在正常的 `select` 模型下出现,因为如果 `select` 返回了有数据可读的通知,那么读取操作应该至少读取到一个字节的数据。
然而,这种情况可能会发生在某些特殊情况下,比如:
1. 硬件故障:例如,串口接收缓冲区可能已满,导致无法将数据写入应用程序的缓冲区,从而导致读取操作返回 0 字节。
2. 并发操作:例如,在多线程或多进程环境中,可能会发生竞争条件,导致文件描述符被多个线程或进程同时操作,从而导致某些读取操作返回 0 字节。
虽然这种情况很少发生,并且在大多数情况下,`select` 返回了有数据可读的通知,确实会从文件描述符中读取到至少一个字节的数据。但在编写健壮的程序时,应该考虑到所有可能的情况,并且要有适当的错误处理机制来应对这些情况的发生。
因此,对于 `num_bytes == 0` 的情况,除了串口关闭外,还可能存在其他特殊情况,需要根据实际情况进行适当的处理。
优秀,但最终有一个点仍然想向您探讨一下,上面您给修复后的代码片段中这一外逻辑:
} else if (num_bytes == 0) {
// 串口关闭或无数据可读
perror("Serial port closed or no data available");
exit(EXIT_FAILURE);
} else {
// 读取出错
perror("Error reading from serial port");
exit(EXIT_FAILURE);
}
上面代码子片段中,} else if (num_bytes == 0) {,是在select模型中通知有数据可以读到,我理解至少有一个byte及以上不会是0个byte,它什么样的情况下会出现:num_bytes == 0,再就是这种情况是串口硬件问题还是软件问题,是否可以了也给它一个修复 的机会呢?和select 模型一样:
if (consecutive_failures >= 5) {
// 连续失败超过5次,发送错误信息到服务器
send_error_to_server();
// 退出程序
exit(EXIT_FAILURE);
}
continue;
还是必须直接exit(EXIT_FAILURE);更为妥当呢?
二是:
} else {
// 读取出错
perror("Error reading from serial port");
exit(EXIT_FAILURE);
}
这部分如果串口读取出错,是否有什么标志位呢,直接放在else中就读取出错,是否有些武断了些,我想要是更谨慎编码,也请给出对应的最后else分支的读取出错的标志判断代码,让这部分代码更加健壮: } else {
// 读取出错
perror("Error reading from serial port");
exit(EXIT_FAILURE);
}
,我的两点要求问完了,您可以按条回复解答的同时,给出最终解决后并后的代码,十分感谢 。
回答:
关于您的第一个问题,`num_bytes == 0` 的情况通常表示从串口读取到的字节数为 0,即读取操作没有返回任何数据。这可能是因为串口关闭或者是没有数据可读。如果您认为这种情况是由于串口硬件问题而不是程序逻辑问题导致的,那么直接退出程序可能是更为合适的选择。因此,直接使用 `exit(EXIT_FAILURE)` 可能更为妥当。
对于第二个问题,如果串口读取出错,通常会返回 -1,并将 `errno` 设置为适当的错误代码。您可以根据 `errno` 的值来进一步判断错误的类型。因此,在 `else` 分支中,您可以添加一个针对 `errno` 的判断,以确定错误的具体原因,并根据需要采取相应的措施。
下面是针对您的两个问题的解答和相应的代码修正:
1. 对于 `num_bytes == 0` 的情况,我们直接退出程序,因为没有读取到任何数据,这可能是串口关闭或没有数据可读。
2. 对于读取出错的情况,我们可以根据 `errno` 的值判断错误的具体原因,并据此决定是否退出程序。这样可以使代码更加健壮,能够更好地处理可能的错误情况。
下面是修改后的代码片段:
```c
// 串口读取函数
void read_serial(Config *config) {
unsigned char buffer[BUFFER_SIZE];
ssize_t num_bytes;
int frame_start_detected = 0;
int recv_index = 0;
int consecutive_failures = 0; // 记录连续失败的次数
struct timeval tv;
fd_set rfds;
while (1) {
// 发送查询指令
send_query(config->serial_fd);
usleep(150000); // 等待150毫秒
// 设置超时时间为3秒
tv.tv_sec = 3;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(config->serial_fd, &rfds);
// 等待串口数据
int retval = select(config->serial_fd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
perror("Error in select");
exit(EXIT_FAILURE);
} else if (retval == 0) {
// 超时,增加连续失败计数器
consecutive_failures++;
if (consecutive_failures >= 5) {
// 连续失败超过5次,发送错误信息到服务器
send_error_to_server();
// 退出程序
exit(EXIT_FAILURE);
}
continue;
} else {
// 读取串口数据
num_bytes = read(config->serial_fd, buffer + recv_index, BUFFER_SIZE - recv_index);
if (num_bytes > 0) {
// 数据读取成功
consecutive_failures = 0; // 重置连续失败计数器
recv_index += num_bytes;
// 打印接收到的十六进制数据
printf("\n\nReceived data: ");
for(int i = 0;i < recv_index; ++i){
printf("%02X ",buffer[i]);
}
printf("\n");
// 判断帧头是否检测到
if (!frame_start_detected) {
for (int i = 0; i < recv_index; ++i) {
if (buffer[i] == 0xAA) {
frame_start_detected = 1;
// 移动已接收数据到缓冲区开头
memmove(buffer, buffer + i, recv_index - i);
recv_index -= i;
break;
}
}
}
// 判断是否接收到完整的数据包
if (frame_start_detected && recv_index >= EXPECTED_PACKET_LENGTH && buffer[recv_index - 1] == 0x55) {
// 去除帧头和帧尾后的数据长度
int data_length = recv_index - 2;
// 进行 CRC 校验,传递数据段给 verifyData 函数
if (verifyData(buffer + 1, data_length)) {
// 提取温度值并打印
float temperature = convertToTemperature(buffer + 1);
printf("Temperature: %.1f\n", temperature);
// 发送POST请求
send_temperature_via_post(temperature);
} else {
printf("CRC 校验失败\n");
}
// 清除已处理数据
recv_index = 0;
frame_start_detected = 0;
}
} else if (num_bytes == 0) {
// 串口关闭或无数据可读
perror("Serial port closed or no data available");
exit(EXIT_FAILURE);
} else {
// 读取出错
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 数据暂时不可用,继续下一次循环
continue
;
} else {
perror("Error reading from serial port");
exit(EXIT_FAILURE);
}
}
}
}
}
```
这样,当 `num_bytes == 0` 时,表示读取操作未返回任何数据,直接退出程序;当读取出错时,我们根据 `errno` 的值判断错误类型,如果是暂时不可用的错误(如 `EAGAIN` 或 `EWOULDBLOCK`),则继续下一次循环;否则,打印错误信息并退出程序。
细节追问:
很专业,您所回答的:
对于 num_bytes == 0 的情况,我们直接退出程序,因为没有读取到任何数据,这可能是串口关闭或没有数据可读。
这一句中,串口关闭可以理解,如下位机在select明明有数据上来了但这一瞬间串口关掉了,没有读取到任何数据。 但是除开这个特殊情况外还有其它情况吗?我理解明明select 侦测到有数据来了,那么这个数据肯定是num_bytes >= 1,怎么就出现明明就通知有数据就读取到0个数据的逻辑判断上来了呢?我有些不理解,难道这个select模型会出现误通知吗?
终极回答:
您提出了一个很好的问题。
在一般情况下,如果使用 `select` 函数或其他 I/O 多路复用机制,并且它返回了通知有数据可读,那么确实会期望从文件描述符中读取到至少一个字节的数据。这是因为 `select` 函数在通知有数据可读时,文件描述符已经准备好被读取了。
因此,对于 `num_bytes == 0` 的情况,通常不会在正常的 `select` 模型下出现,因为如果 `select` 返回了有数据可读的通知,那么读取操作应该至少读取到一个字节的数据。
然而,这种情况可能会发生在某些特殊情况下,比如:
1. 硬件故障:例如,串口接收缓冲区可能已满,导致无法将数据写入应用程序的缓冲区,从而导致读取操作返回 0 字节。
2. 并发操作:例如,在多线程或多进程环境中,可能会发生竞争条件,导致文件描述符被多个线程或进程同时操作,从而导致某些读取操作返回 0 字节。
虽然这种情况很少发生,并且在大多数情况下,`select` 返回了有数据可读的通知,确实会从文件描述符中读取到至少一个字节的数据。但在编写健壮的程序时,应该考虑到所有可能的情况,并且要有适当的错误处理机制来应对这些情况的发生。
因此,对于 `num_bytes == 0` 的情况,除了串口关闭外,还可能存在其他特殊情况,需要根据实际情况进行适当的处理。
[实践OK]php的round函数实现将微秒转换为毫秒
Php/Js/Shell/Go jackxiang 2024-3-9 20:20
$date = microtime(true);// 返回浮点数,小数点前面是当前时间的时间戳,小数点后为当前时间的微秒
var_dump($date);
// 输出:float(1566897763.0927)
PHP的round函数用于四舍五入浮点数。它的作用是返回最接近一个数字的整数,也可以指定小数点后的位数。例如:
$float = 3.14159;
$rounded = round($float, 2); // 返回 3.14
$rounded = round($float); // 返回 3
要求:timestamp 时间戳 :"timestamp": "1709986011923" #13位的毫秒数,时间戳(精确到毫秒),当前时间5分钟内请求有效
实现:
cat sendmsg_2.php
php sendmsg_2.php
1709986816.8926 #获取当前Unix时间戳,包括微秒
1709986816893 #转换为毫秒
[实践OK]在FreeBSD上安装使用unzip
Unix/LinuxC技术 jackxiang 2023-11-16 11:21
Mac下面压缩是zip的,放FreeBSD下面怎么解压呢?
FreeBSD下面安装unzip的方法是:
pkg install -y unzip #注意:-y是安装在unzip的前面才行
二是:Port方式安装:
-->whereis unzip 查找port源
一般是/usr/ports/archivers/unzip
cd /usr/ports/archivers/unzip
make install clean
rehash ---在不重启服务器的条件使安装了的命令生效
就可以用unzip命令了 。。
然后通过ftp上传PHPWind_GBK_5[1].3.zip
用unzip PHPWind_GBK_5[1].3.zip
在服务器上解压。。
===> Options unchanged
/!\ WARNING /!\
You have security/openssl installed but do not have
DEFAULT_VERSIONS+=ssl=openssl set in your make.conf
===> License Info-ZIP accepted by the user
===> unzip-6.0_8 depends on file: /usr/local/sbin/pkg - found
=> unzip60.tar.gz doesn't seem to exist in /usr/ports/distfiles/.
=> Attempting to fetch https://downloads.sourceforge.net/project/infozip/UnZip%206.x%20%28latest%29/UnZip%206.0/unzip60.tar.gz
===> Generating temporary packing list
install -s -m 555 /usr/ports/archivers/unzip/work/unzip60/unzip /usr/ports/archivers/unzip/work/stage/usr/local/bin
cd /usr/ports/archivers/unzip/work/unzip60 && install -s -m 555 funzip unzipsfx /usr/ports/archivers/unzip/work/stage/usr/local/bin
/bin/ln -sf unzip /usr/ports/archivers/unzip/work/stage/usr/local/bin/zipinfo
install -m 555 /usr/ports/archivers/unzip/work/unzip60/unix/zipgrep /usr/ports/archivers/unzip/work/stage/usr/local/bin
install -m 444 /usr/ports/archivers/unzip/work/unzip60/man/unzip.1 /usr/ports/archivers/unzip/work/stage/usr/local/share/man/man1
cd /usr/ports/archivers/unzip/work/unzip60/man && install -m 444 funzip.1 unzipsfx.1 zipgrep.1 zipinfo.1 /usr/ports/archivers/unzip/work/stage/usr/local/share/man/man1
cd /usr/ports/archivers/unzip/work/unzip60 && install -m 0644 README WHERE /usr/ports/archivers/unzip/work/stage/usr/local/share/doc/unzip
====> Compressing man pages (compress-man)
===> Installing for unzip-6.0_8
===> Checking if unzip is already installed
===> Registering installation for unzip-6.0_8
Installing unzip-6.0_8...
===> Cleaning for unzip-6.0_8
来自:https://dude6.com/article/182115.html
FreeBSD下面安装unzip的方法是:
pkg install -y unzip #注意:-y是安装在unzip的前面才行
二是:Port方式安装:
-->whereis unzip 查找port源
一般是/usr/ports/archivers/unzip
cd /usr/ports/archivers/unzip
make install clean
rehash ---在不重启服务器的条件使安装了的命令生效
就可以用unzip命令了 。。
然后通过ftp上传PHPWind_GBK_5[1].3.zip
用unzip PHPWind_GBK_5[1].3.zip
在服务器上解压。。
===> Options unchanged
/!\ WARNING /!\
You have security/openssl installed but do not have
DEFAULT_VERSIONS+=ssl=openssl set in your make.conf
===> License Info-ZIP accepted by the user
===> unzip-6.0_8 depends on file: /usr/local/sbin/pkg - found
=> unzip60.tar.gz doesn't seem to exist in /usr/ports/distfiles/.
=> Attempting to fetch https://downloads.sourceforge.net/project/infozip/UnZip%206.x%20%28latest%29/UnZip%206.0/unzip60.tar.gz
===> Generating temporary packing list
install -s -m 555 /usr/ports/archivers/unzip/work/unzip60/unzip /usr/ports/archivers/unzip/work/stage/usr/local/bin
cd /usr/ports/archivers/unzip/work/unzip60 && install -s -m 555 funzip unzipsfx /usr/ports/archivers/unzip/work/stage/usr/local/bin
/bin/ln -sf unzip /usr/ports/archivers/unzip/work/stage/usr/local/bin/zipinfo
install -m 555 /usr/ports/archivers/unzip/work/unzip60/unix/zipgrep /usr/ports/archivers/unzip/work/stage/usr/local/bin
install -m 444 /usr/ports/archivers/unzip/work/unzip60/man/unzip.1 /usr/ports/archivers/unzip/work/stage/usr/local/share/man/man1
cd /usr/ports/archivers/unzip/work/unzip60/man && install -m 444 funzip.1 unzipsfx.1 zipgrep.1 zipinfo.1 /usr/ports/archivers/unzip/work/stage/usr/local/share/man/man1
cd /usr/ports/archivers/unzip/work/unzip60 && install -m 0644 README WHERE /usr/ports/archivers/unzip/work/stage/usr/local/share/doc/unzip
====> Compressing man pages (compress-man)
===> Installing for unzip-6.0_8
===> Checking if unzip is already installed
===> Registering installation for unzip-6.0_8
Installing unzip-6.0_8...
===> Cleaning for unzip-6.0_8
来自:https://dude6.com/article/182115.html
[实践OK]PHP的CURL支持302/301的跳转的代码写法和Shell下的Curl参数-L实现跳转。
Php/Js/Shell/Go jackxiang 2023-10-29 00:08
问题:
php的这段代码,不支持访问http时redirect到https,如:
访问:http://jtgl.beijing.gov.cn/jgj/lszt/659722/660341/index.html
307 Temporary Redirect导到:https://jtgl.beijing.gov.cn/jgj/lszt/659722/660341/index.html
请将该函数修改成能自动导到https并获取到相应body内容:
//发起https的get请求
function https_get($url,$timeout=120){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_TIMEOUT,$timeout);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 跳过检查
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 跳过检查
$res = curl_exec($curl);
curl_close($curl);
return $res; //返回json对象
}
回答:
要在PHP中自动将HTTP请求重定向到HTTPS并获取响应内容,您可以使用curl来检测HTTP响应头中的重定向,然后执行相应的HTTPS请求。以下是修改后的函数示例:
function https_get($url, $timeout = 120) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // 启用重定向跟随
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($httpCode == 301 || $httpCode == 302) {
$matches = array();
preg_match('/Location:(.*?)\n/', $response, $matches);
$newUrl = trim(array_pop($matches));
if (!empty($newUrl)) {
curl_setopt($curl, CURLOPT_URL, $newUrl);
$response = curl_exec($curl);
}
}
curl_close($curl);
return $response;
}
$url = "http://jtgl.beijing.gov.cn/jgj/lszt/659722/660341/index.html";
$response = https_get($url);
echo $response;
php -a 测试是OK的。
问题二:那直接在shell下用curl命令实现上面307的Location转向获取到转向后的内容,用什么参数实现呢?我记得好像是有相应参数的。
是的,您可以使用`curl`命令行工具来获取重定向后的内容。要实现HTTP 307重定向的自动跟随,您可以使用`-L`或`--location`参数,这会让`curl`自动跟随重定向并获取最终的内容。下面是示例:
使用`-L`参数,`curl`将自动处理HTTP 307 Temporary Redirect,并获取最终的内容。
3XX response code
-L, --location
(HTTP) If the server reports that the requested page has moved to a different location (indicated with a Location: header and a 3XX response code), this option will make
curl redo the request on the new place. If used together with -i, --include or -I, --head, headers from all requested pages will be shown. When authentication is used, curl
only sends its credentials to the initial host. If a redirect takes curl to a different host, it will not be able to intercept the user+password. See also --location-
trusted on how to change this. You can limit the amount of redirects to follow by using the --max-redirs option.
php的这段代码,不支持访问http时redirect到https,如:
访问:http://jtgl.beijing.gov.cn/jgj/lszt/659722/660341/index.html
307 Temporary Redirect导到:https://jtgl.beijing.gov.cn/jgj/lszt/659722/660341/index.html
请将该函数修改成能自动导到https并获取到相应body内容:
//发起https的get请求
function https_get($url,$timeout=120){
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_TIMEOUT,$timeout);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 跳过检查
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 跳过检查
$res = curl_exec($curl);
curl_close($curl);
return $res; //返回json对象
}
回答:
要在PHP中自动将HTTP请求重定向到HTTPS并获取响应内容,您可以使用curl来检测HTTP响应头中的重定向,然后执行相应的HTTPS请求。以下是修改后的函数示例:
function https_get($url, $timeout = 120) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // 启用重定向跟随
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($httpCode == 301 || $httpCode == 302) {
$matches = array();
preg_match('/Location:(.*?)\n/', $response, $matches);
$newUrl = trim(array_pop($matches));
if (!empty($newUrl)) {
curl_setopt($curl, CURLOPT_URL, $newUrl);
$response = curl_exec($curl);
}
}
curl_close($curl);
return $response;
}
$url = "http://jtgl.beijing.gov.cn/jgj/lszt/659722/660341/index.html";
$response = https_get($url);
echo $response;
php -a 测试是OK的。
问题二:那直接在shell下用curl命令实现上面307的Location转向获取到转向后的内容,用什么参数实现呢?我记得好像是有相应参数的。
是的,您可以使用`curl`命令行工具来获取重定向后的内容。要实现HTTP 307重定向的自动跟随,您可以使用`-L`或`--location`参数,这会让`curl`自动跟随重定向并获取最终的内容。下面是示例:
使用`-L`参数,`curl`将自动处理HTTP 307 Temporary Redirect,并获取最终的内容。
3XX response code
-L, --location
(HTTP) If the server reports that the requested page has moved to a different location (indicated with a Location: header and a 3XX response code), this option will make
curl redo the request on the new place. If used together with -i, --include or -I, --head, headers from all requested pages will be shown. When authentication is used, curl
only sends its credentials to the initial host. If a redirect takes curl to a different host, it will not be able to intercept the user+password. See also --location-
trusted on how to change this. You can limit the amount of redirects to follow by using the --max-redirs option.