GO言語でWebAssemblyを試す

WebAssembly(wasm)とはブラウザ上で高速にプログラムを実行する新しい仕様のことです。こちらの説明がとてもわかりやすいものでした。

さて、WikiにあるGetting Startedのとおりに進めます。

ブラウザ上で、RUNボタンを押すとコンソールに「Hello, wasm!」の文字が表示されます。

この程度のことはJavaScriptや他の方法でも容易に実現できますが、WebAssemblyの良いところは実行されるプログラムはコンパイル済みのネイティブコードであり、起動や実行が高速であるということ。

そして、C/C++などの資産をWebアプリケーション化できるというところにあると思います。

各種のサンプルをみると、Canvasを使ったアプリケーションや、中にはGameBoyのエミュレータを公開している方もいます。

その他、ブラウザ上にコンソールを表示してDBMSへ接続するデモもあります。

GO言語(golang)でテキストファイルをAES暗号化する

AES(Advanced Encryption Standard)は強度が極めて高いと言われている共通鍵暗号化方式の一つで、アメリカ政府が政府内の標準として策定した暗号化規格です。

C#とPythonではプログラムを書いたことがありますが、golangではどうするのだろうと思い、作ってみました。

プログラムの主要部分はこちらのサイトを参考にさせていただきました。

以下、暗号化するプログラムです。

package main

import (
	"fmt"
	"io/ioutil"
	"gopkg.in/alecthomas/kingpin.v2"
	"github.com/howeyc/gopass"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
)

func main() {
	var (
		in   = kingpin.Arg("infile", "Input text File Name").Required().String()
		out   = kingpin.Arg("outfile", "Output text File Name").Required().String()
	)

	//コマンドライン引数を受け取る
	kingpin.Parse()
	fmt.Println("input  txt file :", *in)
	fmt.Println("output txt file :", *out)

	for {
			//16文字のパスワード入力
			fmt.Printf("Enter 16 bytes password: ")
			Password, err := gopass.GetPasswdMasked()
			if err != nil{
				fmt.Println(err)
			}
			if len(Password) == 16 {
				//テキストファイル読み込み
				plainText, err := ioutil.ReadFile(*in)
				if err != nil {
					panic(err)
				}
				//鍵の設定
				block, err := aes.NewCipher([]byte(Password)); if err != nil {
					fmt.Println(err)
				}
				//ガロアカウンタモードのブロック暗号
				gcm, err := cipher.NewGCM(block); if err != nil {
					fmt.Println(err)
				}
				//一度きりの初期化ベクトル作成
				nonce := make([]byte, gcm.NonceSize())
				_, err = rand.Read(nonce); if err != nil {
					fmt.Println(err)
				}
				cipherText := gcm.Seal(nil, nonce, plainText, nil) //暗号化
				cipherText = append(nonce, cipherText...) //初期化ベクトルを先頭に付与

				//Base64 Encode
				sEnc := base64.StdEncoding.EncodeToString(cipherText)
				err = ioutil.WriteFile(*out, []byte(sEnc), 0644); if err != nil {
					fmt.Println(err)
				}

				break

			} else {
				fmt.Println("Password not equal 16 bytes")
			}
		}
}
cat test.txt

Hello World !
あいうえお
かきくけこ
①②③④⑤
go run encaes.go test.txt out.txt

input  txt file : test.txt
output txt file : out.txt
Enter 16 bytes password: ****************
cat out.txt

v3rnUlLk5kha6smtAtNq2hDBrOSFQTg8jM702TQv/pgsouvQX9vlmQlWRQEh76kNcz+muHnhKXygBwqarjWNakNKxup+uZ46Jl5x+dmXF8XCAKauUs8RMoks

そして、以下が複合するプログラムです。

package main

import (
	"fmt"
	"io/ioutil"
	"gopkg.in/alecthomas/kingpin.v2"
	"github.com/howeyc/gopass"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
)

func main() {
	var (
		in   = kingpin.Arg("infile", "Input text File Name").Required().String()
		out   = kingpin.Arg("outfile", "Output text File Name").Required().String()
	)

	//コマンドライン引数を受け取る
	kingpin.Parse()
	fmt.Println("input  txt file :", *in)
	fmt.Println("output txt file :", *out)

	for {
			//16文字のパスワード入力
			fmt.Printf("Enter 16 bytes password: ")
			Password, err := gopass.GetPasswdMasked()
			if err != nil{
				fmt.Println(err)
			}
			if len(Password) == 16 {
				//暗号化されたBase64テキストファイルの読み込み
				sEnc, err := ioutil.ReadFile(*in)
				if err != nil {
					panic(err)
				}
				cipherText, _ := base64.StdEncoding.DecodeString(string(sEnc))
				//鍵の設定
				block, err := aes.NewCipher([]byte(Password)); if err != nil {
					fmt.Println(err)
				}
				//ガロアカウンタモードのブロック暗号
				gcm, err := cipher.NewGCM(block); if err != nil {
					fmt.Println(err)
				}
				//先頭に追加された初期化ベクトルを取り出す
				nonce := cipherText[:gcm.NonceSize()]
				plainByte, err := gcm.Open(nil, nonce, cipherText[gcm.NonceSize():], nil); if err != nil {
					fmt.Println(err)
				}
				err = ioutil.WriteFile(*out, []byte(plainByte), 0644); if err != nil {
					fmt.Println(err)
				}

				break

			} else {
				fmt.Println("Password not equal 16 bytes")
			}
		}
}
go run decaes.go out.txt dec.txt

input  txt file : out.txt
output txt file : dec.txt
Enter 16 bytes password: ****************
cat dec.txt

Hello World !
あいうえお
かきくけこ
①②③④⑤

GO言語でExcelを読み書きする

なぜgolang(GO言語)かというと「pythonは遅く(速く実行させる方法もあります)golangは速い」

そして「コンパイル型の言語であり、生成されたネイティブコードはそれを実行する為に何らかの実行環境を用意する必要がない」

さらに「クロスコンパイル言語であり、MacOSでWindowsやLinuxの実行ファイルを生成できる。またその逆も可能である」

ということが魅力だからです。
場面によっては、Pythonではなくgolangで作る必要があるかと思います。

golangの環境構築は様々なサイトに情報があります。
また、Excelを読み書きする為のパッケージはxuriさん公開されています。

読み込むExcel
VisualStudioCodeでgolang

ということで、CSVファイルを読み込んで既存のExcelファイルに放り込む簡単なプログラムを作成してみました。

package main

import (
	"fmt"
	"encoding/csv"
	"os"
	"log"
	"io"
	"strconv"

	"github.com/360EntSecGroup-Skylar/excelize"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	csvfile   = kingpin.Arg("csvfile", "CSV File Name").Required().String()
	excelfile = kingpin.Arg("excelfile", "Excel File Name").Required().String()
	sheetname = kingpin.Arg("sheet", "Excel Sheet Name").Required().String()
	address = kingpin.Arg("address", "Address ex) A1").Default("A1").String()
)

func main() {

	// コマンドラインパラメータを受け取る
	kingpin.Parse()
	fmt.Println("CSV File Name    :", *csvfile)
	fmt.Println("Excel File Name  :", *excelfile)
	fmt.Println("Excel Sheet Name :", *sheetname)
	fmt.Println("Address          :", *address)

	// CSVファイルオープン
	fcsv, err := os.Open(*csvfile)
	if err != nil {
		log.Fatal(err)
	}
	// 貼り付ける先頭アドレスの数値を取得
	col, row, err := excelize.CellNameToCoordinates(*address)

	//Excelファイルオープン
	//エラーの場合は新規作成(ここは要注意)
	//他のプロセスがファイルにアクセスしていてもエラーになる?
	fexcel, err := excelize.OpenFile(*excelfile)
	if err != nil {
		fexcel = excelize.NewFile()
		fmt.Println(err)
		fmt.Println("Created a new book  :", *excelfile)
	}

	// Sheetがなければ作成する
	index := fexcel.GetSheetIndex(*sheetname)
	if index == -1 {
		m := fexcel.NewSheet(*sheetname)
		if m < 0 {
			fmt.Println("sheet create err :", *sheetname)
		}
		fmt.Println("Created a new sheet :", *sheetname)
	}

	// CSVの読み込み
	r := csv.NewReader(fcsv)
	rowcount := 0
	for {
		record, err := r.Read()
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatal(err)
		}
		// CSVの各行毎にExcelのSheetに出力
		for i, v := range record {
			cellName, err := excelize.CoordinatesToCellName(col + i, row + rowcount)
			if err != nil {
				log.Fatal(err)
			}
			val, err := strconv.ParseFloat(v, 64)
			if err != nil {
				fexcel.SetCellValue(*sheetname, cellName, v)
			} else {
				fexcel.SetCellValue(*sheetname, cellName, val)
			}
		}
		rowcount += 1
	}
	err = fexcel.SaveAs(*excelfile)
	if err != nil {
		log.Fatal(err)
	}
}
Book.xlsx