天天看點

golang 小demo --- 一個需求如何實作成代碼

// 需求------将TXT檔案中的資料按照不同省市放進不同的省市檔案中。

// 面向過程思路:建立34個檔案,然後讀取檔案逐行将資料放進通道内,1協程從通道内讀取資料,分析是屬于哪個省市然後寫入檔案中。這個是面向過程的寫法。

// 弊端:可能同時有兩個協程共同寫入一個檔案 造成資料缺失

// 問題:1.如何判斷是否讀取結束,關閉協程,select 兩個channel一個循環資料一個結束讀取2.加鎖防止兩個協程同時寫入每個檔案

// 面向對象思路:建立34個檔案,34個通道,然後讀取檔案逐行分析是哪個省市然後放進對應的通道中,(34個協程從34個通道中讀取資料分析省市寫入對應檔案中,這麼寫和面向過程就一樣了,沒有意義)應該将通道和檔案綁定在一個對象内,接收端一接收資料立即寫入綁定的檔案   還分析個屁呀

// 優點:34個協程一個檔案一個協程  不會有兩個協程寫入一個檔案的錯誤,

// 問題:1.如何判斷這個省的協程是否結束 wg

// 面向思想主要是将檔案和通道綁定  做結構體  将結構體作為參數傳遞

// 最後阻塞wg  和  關閉通道  總是忘記

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strconv"
	"strings"
	"sync"
)

type Provice struct {
	// 省份證前兩位
	Id int
	// 省的名稱
	Name string
	// 對應省的檔案
	FileName *os.File
	// 對應省的通道
	ChanData chan string
}

var wg sync.WaitGroup

func main() {
	ps := []string{
		"北京11", "天津12", "河北13", "内蒙古15", "上海31", "湖北42", "湖南43",
		"廣西45", "海南46", "四川51", "貴州52", "雲南53", "西藏54", "重慶55",
		"陝西61", "甘肅62", "青海63", "甯夏64", "新疆65", "山西14", "遼甯21",
		"吉林22", "黑龍江23", "江蘇32", "浙江33", "河南41", "澳門82", "台灣83",
		"香港81",
	}
	proviceMap := make(map[int]*Provice, 4096) // 這 裡是指針 對象作為參數值 一般都是指針
	for _, v := range ps {
		name := v[:len(v)-2]
		idstr := v[len(v)-2:]
		id, _ := strconv.Atoi(idstr)
		provice := Provice{Id: id, Name: name}
		file, err := os.OpenFile("./info/"+name+".txt", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0666)
		if err != nil {
			panic(name + "檔案建立失敗")
		}
		defer file.Close()
		provice.FileName = file
		provice.ChanData = make(chan string, 4096)
		proviceMap[id] = &provice
		fmt.Println(name + "對象建立完成")
	}
	// 建立34個協程 監聽寫入檔案
	for _, v := range proviceMap {
		wg.Add(1)
		go wirteData(v)
	}
	// 開始讀資料
	file, err := os.Open("info.txt")
	if err != nil {
		panic("打開檔案錯誤")
	}
	defer file.Close()
	bufioReader := bufio.NewReader(file)
	for {
		line, _, err := bufioReader.ReadLine()
		if err == io.EOF {
			fmt.Println("讀取原始檔案資料完畢")
			// 讀取完畢後  循環關閉通道
			for _, v := range proviceMap {
				close(v.ChanData)
			}
			break
		}
		lineStr := string(line) // 轉化字元串
		fmt.Println(lineStr)
		lineArr := strings.Split(lineStr, ",") // 按,分割
		idstr := lineArr[1][0:2]
		id, _ := strconv.Atoi(idstr) // 擷取第二個數組的前二位
		if provice, ok := proviceMap[id]; ok {
			provice.ChanData <- lineStr
		}
	}

	wg.Wait()
	fmt.Println("操作完畢")
}

// 寫入資料
func wirteData(provice *Provice) {
	for data := range provice.ChanData {
		provice.FileName.Write([]byte(data + "\n"))
	}
	wg.Done()
	fmt.Println(provice.Name + "寫入完畢")
}
           
go