- 목적
- 여러 텍스트파일에서 단어를 검색한다.
- 사용법
- find 찾을단어 대상텍스트파일
- 주요패키지
- os, path/filepath, strings, bufio
- 파일 목록을 가져오는 함수
- 아래소스는 실행할때 인수를 받아서, 해당 인수에 해당하는 파일목록을 출력하는 프로그램이다.
//ch26/ex26.1//ex26.1.go
package main
import (
"fmt"
"os"
"path/filepath"
)
func main() {
if len(os.Args) < 3 { // ❶ 실행 인수 개수 확인
fmt.Println("2개 이상의 실행 인수가 필요합니다. ex) ex26.1 word filepath")
return
}
word := os.Args[1] // ❷ 실행 인수 가져오기
files := os.Args[2:]
fmt.Println("찾으려는 단어:", word)
PrintAllFiles(files)
}
func GetFileList(path string) ([]string, error) {
return filepath.Glob(path)
}
func PrintAllFiles(files []string) {
for _, path := range files {
filelist, err := GetFileList(path) // ❸ 파일목록 가져오기
if err != nil {
fmt.Println("파일 경로가 잘못되었습니다. err:", err, "path:", path)
return
}
fmt.Println("찾으려는 파일 리스트")
for _, name := range filelist {
fmt.Println(name)
}
}
}
- 아래의 소스는 파일을 열어서 라인별로 읽어, 화면에 출력하는 소스이다.
// ch26/ex26.2//ex26.2.go
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
if len(os.Args) < 2 { // ❶ 실행 인수 개수 확인
PrintFile("hamlet.txt")
return
}
filename := os.Args[1] // ❷ 실행 인수 가져오기
PrintFile(filename)
}
func PrintFile(filename string) {
file, err := os.Open(filename) // ❶ 파일 열기
if err != nil {
fmt.Println("파일을 찾을 수 없습니다. ", filename)
return
}
defer file.Close() // ❷ 함수 종료 전에 파일 닫기
scanner := bufio.NewScanner(file) // ❸ 스캐너를 생성해서 한 줄씩 읽기
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
- 아래는 test.txt파일의 내용이다.
THE TRAGEDY OF HAMLET, PRINCE OF DENMARK
by William Shakespeare
Fort. Let us haste to hear it
- 아래의 명령을 사용하여 해당 파일의 내용을 한줄씩 읽어서 출력한다.
- 아래는 완성된 소스이다. 파일리스트의 파일을 모두 검색하여 해당 단어에 맞는 줄을 출력해준다.
- LineInfo : 라인번호와 문자열을 저장하는 구조체
- FindInfo : 파일명과 Lineinfo배열
- findInfos = append(findInfos, FindWordInAllFiles(word, path)...) 해당 함수에서 단어를 검색하여 findInfos 넣는다.
- strings.Contains()함수를 사용하여 단어를 찾는다.
//ch26/ex26.3/ex26.3.go
package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
)
// 찾은 라인 정보
type LineInfo struct { // ❶ 찾은 결과 정보
lineNo int
line string
}
// 파일 내 라인 정보
type FindInfo struct {
filename string
lines []LineInfo
}
func main() {
if len(os.Args) < 3 {
fmt.Println("2개 이상의 실행 인수가 필요합니다. ex) ex26.3 word filepath")
return
}
word := os.Args[1] // ❷ 찾으려는 단어
files := os.Args[2:]
findInfos := []FindInfo{}
for _, path := range files {
// ❸ 파일 찾기
findInfos = append(findInfos, FindWordInAllFiles(word, path)...)
}
for _, findInfo := range findInfos {
fmt.Println(findInfo.filename)
fmt.Println("--------------------------------")
for _, lineInfo := range findInfo.lines {
fmt.Println("\t", lineInfo.lineNo, "\t", lineInfo.line)
}
fmt.Println("--------------------------------")
fmt.Println()
}
}
func GetFileList(path string) ([]string, error) {
return filepath.Glob(path)
}
func FindWordInAllFiles(word, path string) []FindInfo {
findInfos := []FindInfo{}
filelist, err := GetFileList(path) // ❶ 파일 리스트 가져오기
if err != nil {
fmt.Println("파일 경로가 잘못되었습니다. err:", err, "path:", path)
return findInfos
}
for _, filename := range filelist { // ❷ 각 파일별로 검색
findInfos = append(findInfos, FindWordInFile(word, filename))
}
return findInfos
}
func FindWordInFile(word, filename string) FindInfo {
findInfo := FindInfo{filename, []LineInfo{}}
file, err := os.Open(filename)
if err != nil {
fmt.Println("파일을 찾을 수 없습니다. ", filename)
return findInfo
}
defer file.Close()
lineNo := 1
scanner := bufio.NewScanner(file) // ❸ 스캐너를 만듭니다.
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, word) { // ❹ 한 줄씩 읽으면 단어 포함 여부 검색
findInfo.lines = append(findInfo.lines, LineInfo{lineNo, line})
}
lineNo++
}
return findInfo
}
- 위의소스는 싱글스레드라서 파일갯수가 많을수록 시간이 걸린다.
- 아래 소스는 해당 문제를 개선하기 위하여 스레드를 사용하였다.
- go FindWordInFile(word, filename, ch) 으로 고루틴을 실행하고,
- 작업이 완료되면 ch <- findInfo 으로 결과를 채널로 넣는다.
//ch26/ex26.4/ex26.4.go
package main
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
)
type LineInfo struct {
lineNo int
line string
}
type FindInfo struct {
filename string
lines []LineInfo
}
func main() {
if len(os.Args) < 3 { //
fmt.Println("2개 이상의 실행인자가 필요합니다. ex) ex26.4 word filepath")
return
}
word := os.Args[1]
files := os.Args[2:]
findInfos := []FindInfo{}
for _, path := range files {
findInfos = append(findInfos, FindWordInAllFiles(word, path)...)
}
for _, findInfo := range findInfos {
fmt.Println(findInfo.filename)
fmt.Println("--------------------------------")
for _, lineInfo := range findInfo.lines {
fmt.Println("\t", lineInfo.lineNo, "\t", lineInfo.line)
}
fmt.Println("--------------------------------")
fmt.Println()
}
}
func FindWordInAllFiles(word, path string) []FindInfo {
findInfos := []FindInfo{}
filelist, err := filepath.Glob(path) // 실행인자 가져오기
if err != nil {
fmt.Println("파일 경로가 잘못되었습니다. err:", err, "path:", path)
return findInfos
}
ch := make(chan FindInfo)
cnt := len(filelist)
recvCnt := 0
for _, filename := range filelist {
go FindWordInFile(word, filename, ch) // ❶ 고루틴 실행
}
for findInfo := range ch {
findInfos = append(findInfos, findInfo) // ❷ 결과 수집
recvCnt++
if recvCnt == cnt {
// all received
break
}
}
return findInfos
}
func FindWordInFile(word, filename string, ch chan FindInfo) {
findInfo := FindInfo{filename, []LineInfo{}}
file, err := os.Open(filename)
if err != nil {
fmt.Println("파일을 찾을 수 없습니다. ", filename)
ch <- findInfo
return
}
defer file.Close()
lineNo := 1
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, word) {
findInfo.lines = append(findInfo.lines, LineInfo{lineNo, line})
}
lineNo++
}
ch <- findInfo
}
'Go' 카테고리의 다른 글
[묘공단] Tucker의 Go 언어 프로그래밍 30장 : RESTful API 서버 만들기 (0) | 2024.03.22 |
---|---|
[묘공단] Tucker의 Go 언어 프로그래밍 29장 : Go언어로 만드는 웹서버 (0) | 2024.03.16 |
[묘공단] Tucker의 Go 언어 프로그래밍 24장~25장 (0) | 2024.02.03 |
[묘공단] Tucker의 Go 언어 프로그래밍 22장~23장 (0) | 2024.02.03 |
[묘공단] Tucker의 Go 언어 프로그래밍 20장~21장 (0) | 2024.02.03 |