[TOC]
# 4.2 數據驗證
> 數據驗證也是Awk程序比較常用的功能之一,通過驗證可以確保數據格式的合法有效性(至少是合理的)。這節包含一些檢驗輸入數據有效性的小示例,通過了解這些示例,我們可以掌握更多Awk的用法。
## 示例: 每列數據類型檢驗
```
#!/usr/bin/awk -f
# colcheck.awk - 檢驗每列數據類型是數字還是字符串類型
# 輸入: 數字或者字符串
# 輸出: 不符合第一行格式類型的行數據
NR==1{
nfld = NF
for(i=1; i<= NF; i++)
type[i] = isnum($i)
} {
if(NF != nfld)
printf("第 %d 行有 %d 列數據而不是 %d 列\n", NR, NF, nfld)
for(i=1;i<=NF;i++)
if(isnum($i) != type[i])
printf("第 %d 行的第 %d 列值為: %s ,與第一行類型不符合\n", NR, i, $i)
}
function isnum(n){ return n~/^[+-]?[0-9]+$/}
```
驗證結果:
```
輸入數據:
1 a 1 a 1
1 1 1 1 1
輸出結果:
第 2 行的第 2 列值為: 1 ,與第一行類型不符合
第 2 行的第 4 列值為: 1 ,與第一行類型不符合
```
## 示例:定界符的檢驗
> 有時候我們編寫內容包含語句塊(例如有開始標記`.START`和結束標記`.END`),但無法通過執行或者自動語法檢查工具驗證正確性時,我們就只能通過人工方式閱讀驗證,這非常低效,所以我們可以利用Awk來自動檢驗編寫的內容是否有效。
```
/^\.START/{
if( p != 0)
printf("錯誤:在第 %d 行的.START 標記出現在了 .START 標記之后\n", NR);
p = 1
}
/^\.END/{
if( p == 0)
printf("錯誤:在第 %d 行的.END 標記前少了 .START 標記\n", NR);
p = 0
}
END{ if( p != 0) printf("錯誤:缺失 .END 標記" }
```
思考問題:
這個示例的定界符沒有考慮定界符嵌套問題,那么如何擴展以下支持嵌套定界符的檢驗呢?
下面是匹配語句塊語法檢驗的Awk程序,可以找到最近缺失匹配的位置:
```
#!/usr/bin/awk -f
# p12check.awk 定界符有效性檢驗
# 檢驗Awk的語句塊{}定界符有效性(適用于C語言或Bash等語法檢驗)
/{/{
srow[p++] = NR
}
/}/{
if( p == 0 )
printf("語法錯誤:第%d行前缺少了{\n", NR)
else
erow[p--] = NR
}
END{
if( p != 0)
printf("語法錯誤:第%d行的}之后缺少與第%d行的{匹配\n", erow[p+1] , srow[p])
}
```
## 示例: 密碼文件檢驗
> 對Linux/Unix系統熟悉的人都知道 `/etc/passwd`文件是保存用戶的密碼文件,存儲了每個用戶的名稱、ID、組和登錄Shell等信息。
passwd文件格式固定為冒號(:)分隔的7個字段。
```
用戶名:加密的密碼串:用戶ID:組ID:全名:home目錄:登錄默認運行的程序
```
下面編寫一段`pwdcheck.awk`檢查`passwd`文件正確性腳本:
```
# pwdcheck.awk 檢查passwd文件
BEGIN{ FS=":"}
NF != 7{ printf("第%d行不是7列數據:%s\n",NR,$0)}
$1~/[^A-Za-z0-9]/{ printf("第%d行用戶名不是字母或數字組合:%s\n",NR,$0)}
$2 == ""{ printf("第%d行用戶密碼為空:%s\n",NR,$0)}
$3 ~/[^0-9]/{ printf("第%d行用戶ID非數字:%s\n",NR,$0)}
$4 ~/[^0-9]/{ printf("第%d行組ID非數字:%s\n",NR,$0)}
$6 !~/^\//{ printf("第%d行用戶HOME目錄不是絕對路徑:%s\n",NR,$0)}
```
這是一個密碼文件檢驗的簡單示例,當我們需要時可以繼續修改或添加新的規則條件,每日執行檢驗保證密碼文件的合法性。
## 示例:數據校驗程序生成器
> 剛才密碼文件檢驗程序是手工編寫的,那么我們接下來看一個簡單的自動生成Awk檢驗程序的示例。
```
BEGIN{ FS = "\t+" }
{ printf("%s {\n\tprintf(\"line %%d, %s: %%s\\n\", NR,$0)\n}\n",$1,$2)}
```
在終端執行結果:
```
## 執行方法
$ ./checkgen.awk rule.list
## 輸入內容:
$ cat rule.list
NF != 7 不包含7列數據
$1 ~ /[^A-Za-z0-9]/ 用戶ID非數字
$2 == "" 沒有設置密碼文件
##輸出結果:
NF != 7 {
printf("line %d, 不包含7列數據: %s\n", NR,$0)
}
$1 ~ /[^A-Za-z0-9]/ {
printf("line %d, 用戶ID非數字: %s\n", NR,$0)
}
$2 == "" {
printf("line %d, 沒有設置密碼文件: %s\n", NR,$0)
}
```
這個示例是一個抽象提取規則的示例,提取出模式串和提示信息串兩部分作為輸入數據,編寫通用的生成檢驗腳本規則,這樣可以達到編寫一個腳本可以重復利用的效果,這樣可以減少我們很多重復性勞動工作。
- 目錄
- 概述
- 第一章 編寫第一個Awk命令
- 1.1 什么是Awk命令
- 1.2 第一個Awk命令
- 第二章 Awk的模式匹配
- 2.1 Awk模式語法規則
- 2.2 Awk模式規則詳解
- 第三章 Awk的動作規則
- 3.1 Awk動作匹配語法規則
- 3.2 Awk動作規則詳解
- 第四章 Awk數據處理方法
- 4.1 數據轉換和提煉
- 4.2 數據驗證
- 4.3 數據打包與拆包處理
- 4.4 多行數據處理
- 4.5 隨機數生成
- 第五章 Awk的輸出報告和腳本封裝
- 5.1 輸出報告
- 5.2 封裝查詢結果和報告
- 第六章 Awk實現排序算法
- 6.1 插入排序算法實現
- 6.2 快速排序算法實現
- 6.3 堆排序算法實現
- 6.4 拓撲排序算法實現
- 總結