10G數據不用框架快速去重

試想一下,如果有10G數據,或者更多;怎麼才能夠快速地去重呢?你會說將數據導入到數據庫(mysql等)進行去重,或者用java寫個程序進行去重,或者用Hadoop進行處理。如果是大量的數據要寫入數據庫也不是一件容易的事情,首先你需要開發一個程序將數據寫入數據庫,然後再用數據庫的select distinct或者group by進行去重。如果是一次性的工作,這種方式顯得就比較笨拙了。那麼有沒有更好的辦法呢?下面記錄一下我是怎麼從10G數據裏面迅速去重的。這裏採用shell腳本的方式進行處理。

說明

  • 這10G數據分佈在七八個文件中

  • 文件編碼格式爲gb2312

  • 數據格式:

"ID","已採","已發","內容","PageUrl"
"1","1","1","====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||","http://www.xxx.com/"
"1","1","1","====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||","http://www.xxx.com/"
"1","1","1","====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||","http://www.xxx.com/"



數據格式說明:

  • 上面是3行數據
  • 每行都是5個field
  • 每個field間用“,”隔開
  • 而需要的內容在第4個field(也就是內容這個field)
  • 內容field間會員用“ ==================== ”隔開
    要達到的效果:將會員信息提取出來並去重

實現思路和測試

1.由於文件編碼是gb2312編碼,所以第一步是將文件編碼轉成utf-8編碼

iconv -c -f gb2312 -t utf-8 test.txt > test2.txt

這裏我們用linux系統自帶的iconv命令進行轉換,-c的意思是將無法識別的字符直接忽略掉

2、獲取會員信息,由於是第4個field,因而我們只需要獲取第四個field即可,這裏採用awk命令獲取

awk 'BEGIN{ FS=",";}{ print $4 }' test2.txt > test_menber.txt

awk指定分隔符的方法爲FS=”,”,運行後的結果如下

vi test_menber.txt
"內容"
"====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||"
"====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||"
"====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||====================會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||"

這裏已經獲取到了會員的信息;但是一行有多個會員的信息,而個數又是不固定的。因此下面是想辦法一行顯示一個會員

3、一行顯示一個會員

由於會員信息間通過“====================”分割,要實現一行顯示一個會員只需要將“====================”替換成換行即可。

sed -i "s/====================/\n/g" test_menber.txt

查看test_menber.txt的內容

vi test_menber.txt 
  1 "內容"
  2 "
  3 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
  4 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
  5 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
  6 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||"
  7 "
  8 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
  9 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
     10 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
     11 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||"
     12 "
     13 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
     14 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
     15 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||
     16 會員名:鬼腳七||||||||||||||||||會員等級:軍長第5年|||姓名:張三 |||"



4、替換掉其他字符“””、空行和“|||”

#將“"”替換成空字符串
sed -i "s/\"//g" test_menber.txt
#刪除空行
sed -i '/^$/d' test_menber.txt
#將分隔符|||換成@
sed -i "s/|||/@/g" test_menber.txt



5、根據會員名去重

sort -t $"@" -k 1,1 -su test_menber.txt >test_uniq.txt

-t可以指定分隔符,-k指定排序的field, -su表示穩定排序並去重

查看結果

[root@bogon yichen]# more test_uniq.txt 
會員名:鬼腳七@@@@@@會員等級:軍長第5@姓名:張三 @
內容

思路和測試完成了,但是文件比較多,一個個文件去處理也比較麻煩。因而寫個腳本去處理即可

腳本批量處理

1、轉換文件編碼

[root@bogon yichen]# vi iconv_shell.sh 
#!/bin/sh
if [ "$#" != "2" ]; then
echo "Usage: `basename $0` dir filter"
exit
fi
echo $1
for file in `find $1 -name "$2"`; do
echo "$file"
iconv -c -f gb2312 -t utf8 $file > ${file}_utf8

調用./iconv_shell.sh 目錄 文件通配符,例如:

./iconv_shell.sh ./ "*txt"

此時生成的文件後綴爲:.txt_utf8

2、提取會員信息

vi awk_filler.sh 
#!/bin/sh
if [ "$#" != "2" ]; then
echo "Usage: `basename $0` dir filter"
exit
fi
for file in `find $1 -name "$2"`; do
echo "$file"
awk 'BEGIN{ FS=",";}{ print $4 }' $file > ${file}_menber
done

調用

./awk_filler.sh ./ "*.txt_utf8"

此時生成的文件後綴爲:.txt_utf8 _menber

3、替換“ ==================== ”、“””、“|||”和換行

vi sed_shell.sh 
#!/bin/sh
if [ "$#" != "2" ]; then
echo "Usage: `basename $0` dir filter"
exit
fi
for file in `find $1 -name "$2"`; do
echo "$file"
sed -i "s/====================/\n/g; s/\"//g; /^$/d; s/|||/@/g" $file
done

for file in `find $1 -name "$2"`; do
echo "$file"
sed -i "/^$/d" $file
done

sed支持多表達式:sed “表達式1;表達式2” filename,注意表達式之間用“;”號隔開。

調用:

sh ./sed_shell.sh ./ "*.txt_utf8_menber"

替換後的文件後綴仍爲txt_utf8_menber

4、去重

vi uniq_shell.sh 
#!/bin/sh
if [ "$#" != "2" ]; then
echo "Usage: `basename $0` dir filter"
exit
fi
for file in `find $1 -name "$2"`; do
echo "$file"
sort -t $"@" -k 1,1 -su ${file} >${file}_uniq
done

調用:

sh ./uniq_shell.sh ./ "*.txt_utf8_menber"

最後生成去重後的文件後綴爲txt_utf8_menber_uniq

其實也可以放在一個shell腳本里面完成,這裏就不再闡述了。想想看10G的文件用幾個簡單的命令就完成了去重,可見linux的強大。所以說學幾個簡單的linux命令還是很有必要的,這樣可以大大地降低你的工作量,提高工作的效率。

發佈了107 篇原創文章 · 獲贊 147 · 訪問量 51萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章