ついにC言語のプロジェクトへメインプログラマとして(C言語初心者)

C言語プロジェクト。しかもチューニング系。
やばし。マクロとかまだぜんぜんわかってない。まぁ、なんとかするでしょ。私なら
20時間ぐらいGStreamerというライブラリについて右往左往した。
Macはなんかフル機能つかえないっぽい。VMWindowsでも使えないっぽい。
結局DAW用のWin7マシンにVS2017Community入れてTeamViewer経由できちんと動いた。
再生とスクリーンショットを取れたところまで。
なんだかムダな努力を積み重ねた気がするよ。終末吹っ飛んで辛い。
どうやってC言語まなんで行こうか思案中
Cを学ぶIDEEclipseで良いですかね。。。補完効くのかしら?効かないとやだなぁ。

github.com

正規表現 AND

(?=.*lemon)(?=.*apple)

は下記のすべての行にマッチ

lemon, apple, grape, orange
apple, lemon, grape, orange
grape, apple, lemon, orange

いやー知らなかったよー。

Golangでスクレイピング(Rubyの225倍速)

Go速い。並列処理速い。
rubyの実装だと15分かかっていた処理が4.5秒へ。ヤバすぎるぜGo!!
もう、本当にrubyは書きやすいシェルスクリプトぐらいの立ち位置で良いんじゃないかな。
テストデータ作る処理とかだけで。
他はPythonとかGoで書くと良いとおもいましたよ。
スクレイピングはいかに効率を上げるかが重要ですが、Goの並行処理の仕組みは使いやすさも含めてすごい良いです。
ともかく速さは命。

package main

/*
実装方針
とりまDB登録とかしちゃうのはやめておいて、
logger作るのはやめておいて
JSONから設定読み込んで出力するとか
goqueryで一回叩いてみるとか、その辺を2017/06/10はするところまで

チャンネルとかデータ構造とかは2017/06/11から少しずつやっていけばよろし。
DB以外の部分はとりあえず出来た。
めっちゃ速いwwww
rubyだと900秒
goだと4.5秒
*/

import (
	"encoding/json"
	f "fmt"
	"github.com/PuerkitoBio/goquery"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
)

const StartURL = "http://youtubeanisoku1.blog106.fc2.com/"
const Configfile = "config.json"

const (
	ANISOKUTOP int = iota
	KOUSINPAGE
	KOBETUPAGE
	HIMADOSEARCH
	HIMADOVIDEO
)

type Config struct {
	DonloadDir  string `json:"downloaddir"`
	DBFILE      string `json:"dbfile"`
	TITLEREGEXP string `json:"title_regexp"`
}

var cfg Config

type JOB struct {
	JOBKIND int
	URL     string
	TITLE   string
	EPISODE string
}

func (job *JOB) Dispacher() {
	switch job.JOBKIND {
	case ANISOKUTOP:
		wg.Add(1)
		go job.AnisokuTop()
	case KOUSINPAGE:
		wg.Add(1)
		go job.KousinPage()
	case KOBETUPAGE:
		wg.Add(1)
		go job.KobetuPage()
	case HIMADOSEARCH:
		wg.Add(1)
		go job.HimadoSearch()
	case HIMADOVIDEO:
		wg.Add(1)
		go job.HimadoVideo()
	default:
	}

}

// トップページのスクレイピング
func (job *JOB) AnisokuTop() {
	defer wg.Done()
	doc, err := goquery.NewDocument(job.URL)
	if err != nil {
		f.Println("url scraping fail:", job.URL)
		return
	}
	doc.Find(".Top_info div ul li a").Each(func(_ int, s *goquery.Selection) {
		title, _ := s.Attr("title")
		if strings.Contains(title, "更新状況") {
			u, _ := s.Attr("href")
			JobCh <- &JOB{KOUSINPAGE, u, "", ""}
		}
	})
}

// 更新ページのスクレイピング
func (job *JOB) KousinPage() {
	defer wg.Done()
	doc, err := goquery.NewDocument(job.URL)
	if err != nil {
		f.Println("kousinpage error", job.URL)
		return
	}
	doc.Find("ul[type='square'] li a").Each(func(_ int, s *goquery.Selection) {
		href, _ := s.Attr("href")
		if href == "" {
			return
		}
		title, _ := s.Attr("title")
		if title != "" {
			return
		}
		JobCh <- &JOB{KOBETUPAGE, href, "", ""}
	})
}

// 個別ページのスクレイピング
func (job *JOB) KobetuPage() {
	defer wg.Done()
	doc, err := goquery.NewDocument(job.URL)
	if err != nil {
		f.Println("kobetupage error", job.URL)
		return
	}
	var title string
	doc.Find("title").Each(func(_ int, s *goquery.Selection) {
		title = s.Text()
		title = cleanupValue(title)
	})

	if !TitleRegexp.MatchString(title) {
		return
	}

	_, ok := TitleMap[title]
	if ok {
		return
	} else {
		TitleMap[title] = true
	}

	//f.Println("DO:", title)

	found := false
	doc.Find("a").Each(func(_ int, s *goquery.Selection) {
		if found {
			return
		}
		href, _ := s.Attr("href")
		if !strings.Contains(href, "himado.in") {
			return
		}
		//一つ見つかればOK
		if !found {
			JobCh <- &JOB{HIMADOSEARCH, href, title, ""}
			found = true
		}
	})
}

// ひまわり検索ページ
func (job *JOB) HimadoSearch() {
	defer wg.Done()
	doc, err := goquery.NewDocument(job.URL)
	if err != nil {
		f.Println("himadosearch error", job.URL)
		return
	}

	count := 0
	doc.Find(".thumbtitle a[rel='nofollow']").Each(func(_ int, s *goquery.Selection) {
		if count > 2 {
			return
		}
		href, _ := s.Attr("href")
		if href == "" {
			return
		}
		href = "http://himado.in" + href
		// 再取得制御
		_, exist := PageMap[href]
		if exist {
			return
		}
		PageMap[href] = true
		episode, _ := s.Attr("title")
		if episode == "" {
			return
		}
		episode = cleanupValue(episode)
		JobCh <- &JOB{HIMADOVIDEO, href, job.TITLE, episode}
		count++
	})
}

// ひまわりビデオページ
func (job *JOB) HimadoVideo() {
	defer wg.Done()
	doc, err := goquery.NewDocument(job.URL)
	if err != nil {
		f.Println("himadoVideo error", job.URL)
		return
	}
	mediaUrl := ""
	doc.Find("script").Each(func(_ int, s *goquery.Selection) {
		text := s.Contents().Text()
		if text == "" {
			return
		}
		texta := strings.Split(text, "\n")
		for _, l := range texta {
			if strings.Contains(l, "var movie_url") {
				l = strings.TrimSpace(l)
				l = strings.Replace(l, "var movie_url = '", "", -1)
				l = strings.Replace(l, "';", "", -1)
				u, err := url.PathUnescape(l)
				if err == nil {
					mediaUrl = u
				}
				break
			}
		}
	})
	if mediaUrl == "" {
		return
	}
	fp := makeFilePath(job.TITLE, job.EPISODE)
	if !FileIsExists(fp) {
		return
	}
	job.DownloadVideo(mediaUrl)
}

// ビデオダウンロード
func (job *JOB) DownloadVideo(url string) {
	err := os.MkdirAll(makeFileDirPath(job.TITLE), 0777)
	if err != nil {
		f.Println("ディレクトリ作成失敗")
		return
	}
	fp := makeFilePath(job.TITLE, job.EPISODE)
	cmd := "curl -# -L " + url + " | ffmpeg -threads 4 -y -i - -vcodec copy -acodec copy '" + fp + "' &"
	f.Println(cmd)
	exec.Command("sh", "-c", cmd).Start()
}

// ディレクトリを確認
func makeFileDirPath(title string) string {
	return filepath.Join(cfg.DonloadDir, title)
}

// ファイルパスを作成
func makeFilePath(title string, episode string) string {
	return filepath.Join(cfg.DonloadDir, title, episode+".mp4")
}

// ファイル存在確認
func FileIsExists(filename string) bool {
	_, err := os.Stat(filename)
	return err != nil
}

// 値をきれいにする
func cleanupValue(s string) string {
	s = strings.Replace(s, "★ You Tube アニ速 ★", "", -1)
	s = strings.Replace(s, ":", ":", -1)
	s = strings.Replace(s, "第", "", -1)
	s = strings.Replace(s, "話", ":", -1)
	s = strings.Replace(s, ".", "", -1)
	s = strings.Replace(s, " ", "", -1)
	s = strings.Replace(s, " ", "", -1)
	s = strings.Replace(s, "#", "", -1)
	s = strings.Replace(s, "(", "", -1)
	s = strings.Replace(s, ")", "", -1)
	s = strings.Replace(s, "/", "", -1)
	s = strings.Replace(s, "(", "", -1)
	s = strings.Replace(s, ")", "", -1)
	s = strings.Replace(s, "+", "+", -1)
	s = strings.Replace(s, "[720p]", "", -1)
	s = strings.Replace(s, "高画質", "", -1)
	s = strings.Replace(s, "QQ", "", -1)
	s = strings.Replace(s, "?", "?", -1)
	s = strings.Replace(s, "[", "", -1)
	s = strings.Replace(s, "]", "", -1)

	return s
}

// 確認済み管理マップ
var TitleMap map[string]bool = make(map[string]bool)
var PageMap map[string]bool = make(map[string]bool)
var TitleRegexp *regexp.Regexp

// JOBチャネル
var JobCh chan *JOB = make(chan *JOB)

// WaitGroup
var wg sync.WaitGroup = sync.WaitGroup{}

// コンフィグを読み出す
func loadConfig() (*Config, error) {
	fh, err := os.Open(Configfile)
	if err != nil {
		return nil, err
	}
	defer fh.Close()
	err = json.NewDecoder(fh).Decode(&cfg)
	TitleRegexp = regexp.MustCompile(cfg.TITLEREGEXP)
	return &cfg, err
}

// JOBチャネルのレシーバー
func receiver(ch chan *JOB) {
	for {
		job := <-ch
		job.Dispacher()
	}
}

// 本体
func Run() int {

	// コンフィグ読み出し
	cfg, err := loadConfig()
	if err != nil {
		f.Println("config load error", err)
		return 1
	}
	f.Println(cfg)

	go receiver(JobCh)

	// 初期キック
	JobCh <- &JOB{ANISOKUTOP, StartURL, "", ""}

	wg.Wait()
	return 0
}

func main() {
	retcode := Run()
	os.Exit(retcode)
}

Golang コードを書き始めた

リファレンスを参照しながら少しずつ作りはじめてる

スクレイピング系のプログラムを作ることにした。
とりま、main.goだけでいく。今のところ。structの分離とかはおいおいやっていけば良いかなと。
rubyで書いていたコードのgo移植なので細かいdomの確認とかはrubyのものが流用できるので結構純粋にGolangだけの取り組みなので良い題材かなと
初めてのGoのプログラミングだけど1時間ぐらいで骨子が見えてきているのはなかなか良いペースだと自画自賛
やっぱ書かないと身につかないね。

package main

/*
実装方針
とりまDB登録とかしちゃうのはやめておいて、
logger作るのはやめておいて
JSONから設定読み込んで出力するとか
goqueryで一回叩いてみるとか、その辺を2017/06/10はするところまで

チャンネルとかデータ構造とかは2017/06/11から少しずつやっていけばよろし。
=>ちょっとやりはじめてる
*/

import (
	f "fmt"
	"os"
	"strings"
	"encoding/json"

	"github.com/PuerkitoBio/goquery"
)

const StartURL = "http://youtubeanisoku1.blog106.fc2.com/"
const Configfile = "config.json"
const (
	KOUSINPAGE int = iota
	KOBETUPAGE
	HIMADOSEARCH
	HIMADOVIDEO
)

type Config struct {
	DonloadDir  string `json:"downloaddir"`
	DBFILE      string `json:"dbfile"`
	TITLEREGEXP string `json:"title_regexp"`
}
var cfg Config

type JOB struct {
	JOBKIND int
	URL     string
}

// チャンネル
var JobCh chan *JOB

// コンフィグを読み出す
func loadConfig() (*Config, error) {
	f, err := os.Open(Configfile)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	err = json.NewDecoder(f).Decode(&cfg)
	return &cfg, err
}

// ディスパッチャー
func dispatcher(ch <-chan *JOB) {
	for {
		job := <-ch
		f.Println(job)
	}
}

// 本体
func Run() int {
	// コンフィグ読み出し
	cfg, err := loadConfig()
	if err != nil {
		f.Println("config load error", err)
		return 1
	}
	f.Println(cfg)

	// JOBのチャンネル
	JobCh = make(chan *JOB)
	go dispatcher(JobCh)

	// 初期キック
	doc, err := goquery.NewDocument(StartURL)
	if err != nil {
		f.Println("url scraping fail:", StartURL)
		return 1
	}
	doc.Find(".Top_info div ul li a").Each(func(_ int, s *goquery.Selection) {
		title, _ := s.Attr("title")
		if strings.Contains(title, "更新状況") {
			url, _ := s.Attr("href")
			JobCh <- &JOB{KOUSINPAGE, url}
		}
	})

	return 0
}

func main() {
	retcode := Run()
	os.Exit(retcode)
}

みんなのGo言語読んだ

さらっと流してよんだ。実際にものをつくってから立ち帰ってベスト・プラクティスを知るためにもう一度読めば良い本だと思う。逆引きまでは行かないまでも良い網羅感あり。

さぁ、プログラムを組もう。楽しもう。そろそろウズウズしてきてるよ。

みんなのGo言語【現場で使える実践テクニック】

みんなのGo言語【現場で使える実践テクニック】

Golang 金曜の夜はテストコード

とりま仕事から帰って趣味の事した。
本を読んで、ギターを弾いて

コード書かなきゃね。
草生やさないと。

GOPATH内にgoglandのプロジェクト持ってきた

~/path/to/go/src/gogland/project
とようになるようにした

テスト

@foo.go

package foo

func IsOne(n int) bool {
	if n == 1 {
		return true
	} else {
		return false
	}
}

@foo_test.go

package foo

import (
	"testing"
)

func TestIsOne(t *testing.T) {
	n := 1
	b := IsOne(1)
	if b != true {
		t.Errorf("%d is not one", n)
	}
}

テスト実行

go test gogland/project/foo

Golang エラー発生させ

package main

import(
	f "fmt"
)

type MyError struct {
	Message string
	ErrCode int
}

func (e *MyError) Error() string {
	return e.Message
}

func RaiseError() error {
	return &MyError{"深刻なエラーが発生しました", 1234}
}

func main() {
	err := RaiseError()
	// 型アサーション
	e, ok := err.(*MyError)
	if ok {
		f.Println(e.ErrCode)
	}
}