[go] お客様の中にGoを極めた方はいらっしゃいませんかー?

golangでも同じことをやってみた

[c#] SJISというかCP932とかいう呪い

c#でのcp932で化けてしまう一部キャラクタを変換するやつ。それをgoで書いてみたのだけど……。なんだろ、遅い。正規表現は他言語と同じく遅いようで、RegexpではなくReplacer使えーとかそこらへんは考慮して書いたのだが。stopwatchの精度もスリープで確認したりなんだりで問題なさそう。未使用メソッドがあったり、処理の1,2,3を順にループさせたりするとどんどん遅くなる(なので未使用~についてはコメントアウトしてる)のが謎い。

👺 < 処理が遅い!

4826            // -> goでのこのミリセカンド表記はc#版のほうでいうと
00:00:04.0826   // -> これと同値

最終結果は同じだが、文字化け(マッピングできないキャラ)が来るとそこで試合終了っぽい。

結果

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
    "time"

    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

// ループの外でReplacerを持つようにした
var rep *strings.Replacer

func main() {
    fmt.Println("start")
    // ファイル読み込み
    list := getFileData()
    /*list2 := make([]string, len(list))
    copy(list2, list)
    list3 := make([]string, len(list))
    copy(list3, list)*/
    // 処理
    start1 := time.Now()
    // ループの外でReplacerを持つようにした
    rep = strings.NewReplacer("〜", "~", "−", "-", "¢", "¢", "£", "£", "¬", "¬", "—", "―", "‖", "∥")
    for i := 0; i < 1000000; i++ {
        replaceUnmappingChars1(list)
    }
    stop1 := time.Now()
    fmt.Println(stop1.Sub(start1).Milliseconds())
    // ファイル書き込み
    outputFile(list)
    fmt.Println("end")
}

// replace unmapping chars3 -> 8293
/*func replaceUnmappingChars3(list []string) {
    for i, str := range list {
        sl := strings.Split(str, "")
        length := len(sl)
        var rep string
        for j := 0; j < length; j++ {
            if sl[j] == "〜" {
                rep += "~"
            } else if sl[j] == "−" {
                rep += "-"
            } else if sl[j] == "¢" {
                rep += "¢"
            } else if sl[j] == "£" {
                rep += "£"
            } else if sl[j] == "¬" {
                rep += "¬"
            } else if sl[j] == "—" {
                rep += "―"
            } else if sl[j] == "‖" {
                rep += "∥"
            } else {
                rep += sl[j]
            }
        }
        list[i] = rep
    }
}*/

// replace unmapping chars2 -> 100万回 9946
/*func replaceUnmappingChars2(list []string) {
    for i, str := range list {
        var rep string
        for _, c := range str {
            if string(c) == "〜" {
                rep += "~"
            } else if string(c) == "−" {
                rep += "-"
            } else if string(c) == "¢" {
                rep += "¢"
            } else if string(c) == "£" {
                rep += "£"
            } else if string(c) == "¬" {
                rep += "¬"
            } else if string(c) == "—" {
                rep += "―"
            } else if string(c) == "‖" {
                rep += "∥"
            } else {
                rep += string(c)
            }
        }
        list[i] = rep
    }
}*/

// replace unmapping chars -> 100万回 4826
func replaceUnmappingChars1(list []string) {
    for i, data := range list {
        //rep := strings.NewReplacer("〜", "~", "−", "-", "¢", "¢", "£", "£", "¬", "¬", "—", "―", "‖", "∥")
        list[i] = rep.Replace(data)
    }
}

// GetFileData return list
func getFileData() []string {
    // file open
    file, err := os.Open("./source.txt")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    defer file.Close() // c#でのusing - disposeみたいな? todo:あとでもっとよく調べる
    // file read
    var data []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        data = append(data, scanner.Text())
    }
    if err := scanner.Err(); err != nil { // どうやったらここに来るんだ?
        fmt.Println(err)
        os.Exit(1)
    }
    return data
}

// outputFile
func outputFile(list []string) {
    // file create todo:上書きっぽい(あとでよく見る)
    file, err := os.Create("./destination_sjis_fix.txt")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    defer file.Close()
    // data write
    writer := bufio.NewWriter(transform.NewWriter(file, japanese.ShiftJIS.NewEncoder()))
    for _, data := range list {
        _, err := writer.WriteString(data + "\n") // やっぱり普通はここで変換するよね(c#でも書いたのでこっちにも)
        if err != nil {
            fmt.Println(err)
            os.Exit(1)
        }
    }
    writer.Flush() // buf(debuggerでみた)の中にWriteStringで溜めてFlushで一気に書くっぽい
}

なんか根本的にミスってるのかなー。わからん。ベンチマークメソッド?みたいなのがあるからそっちでやらないとダメなのかなー。

[go] 既存のPhalcon製mailformをgolangで書き直した

メールフォームを作り直した

[PHP Phalcon 3.4] メールフォーム作った
ちょうど一年くらい前に作ったのですが、迷惑メールに突っ込まれまくっていたため何通か頂いたのに反応できませんでした。せっかくこんな廃墟に来ていただいたというのに。

PHP Phalcon -> GO

そんなわけで自鯖からのphp mb_send_mailではなく、sendgridを使ってgoから飛ばすように変えました。LocalのPHP環境ではPhalconの4.xにしていたのですが、既存の3.4のメールフォーム、なんと全く動かず。上のエントリからリポジトリ見てもらえばわかるのですが、俗に言うMicroでさえこの有様。ちょっと面倒ですね。

というわけでPHPやめて、勉強しようしようと思っていたgoで同じものを書いてみました。CSRF周りはginにもあるようですが、Phalconと同じようにしたかったので自前実装です。ロジックの中でhtmlタグ生成するの嫌いなんですよね……。goでのWebアプリというと、SPAのバックで使う人が多いのでAPI鯖みたいなのが多いのですが、今回は完全にtemplatesを使ったベッタベタなWebアプリです。

https://bitbucket.org/dobusarai/go-mailform/src/master/

readmeかったるくて自動生成まんまなのさすがにマズイな。

噂には聴いていましたが、モダンな言語にある機能?が本当に無いので驚いた。でもこのレベルなら慣れの範疇だなー。ただgo modは前エントリにも書いたけどアレだと思う。あと、この量のコードでhttpd含めて動いてしまうのは凄いな、と。

https://bitbucket.org/dobusarai/go-mailform/src/master/main.go

たった252行だよ。(templates等は除く)

すでに /contact はこれで動いています。いないと思うけど使う人は、settings.ini.dummyをsettings.iniにリネームして中身書き換えればそのまま動きます。

結局

お気づきの方は初っ端からお気づきだと思いますが、sendgridにしようがなんだろうがSPF/DKIMの設定からは逃れられないのです。あー。SendGridのDomain Authenticationまわりを設定しないとダメですね。あー、めんどくさい。

[go] 5分でできる sendgrid apiでメール送信

SendGridさんのアカウント開設

アカウント作成(*1) -> コンパネメニュー -> Settings -> API Keys -> Create API Key(右上ボタン) -> 適当にAPI Keyの名前つけて Create&Viewボタン押下 -> API Keyをメモっておく

*1
アカウントを作成するのに審査があります。私はいくつかの選択肢の中からサイト作成を選択しました。ご担当者さんのアナウンスといい作業はとってもスムーズに終了。ちなみに表題の5分にはこの申請についての時間は含んでいません。つまりインチキです。

SendGridさんのサンプルを参照したが...

Goでメール送信!SendGridを使って簡単に実装する方法
書いててなんかおかしいなと思ったら、この記事5年以上前のものです。

スクラッチで

1: ディレクトリ(sendgrid-example1)作る
2: 上のディレクトリに移動してgomod初期化とパッケージインストール

go mod init sendgrid-example1
go get -u github.com/sendgrid/sendgrid-go

3: コード

package main

import (
    "fmt"

    "github.com/sendgrid/sendgrid-go"
    "github.com/sendgrid/sendgrid-go/helpers/mail"
)

// 記述されてるアドレスは架空のものです
func main() {
    from := mail.NewEmail("dobusarai info", "info@dobudobuo.net")
    subject := "mail test subject"
    to := mail.NewEmail("hogehoge", "dobusarai@dobudobuo.net")
    plainTextContent := "and easy to do anywhere, even with Go"
    htmlContent := "<strong>and easy to do anywhere, even with Go</strong>"
    message := mail.NewSingleEmail(from, subject, to, plainTextContent, htmlContent)
    client := sendgrid.NewSendClient("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX<API KEY>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
    response, err := client.Send(message)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(response.StatusCode)
        fmt.Println(response.Body)
        fmt.Println(response.Headers)
    }
}

goをちょっと学びながら書いているが、初学者殺しのクソブログエントリが多すぎる。結局githubのreadmeとソースコードを読むのが一番速く済みますね。英語はDeepLさんの出番です!あ、goはシングル(ファイル)バイナリになったりコンパイル速度とか良いところ多いけど、やはり例外がないので書くのかったるい。あとgo modulesはクソだと思う。これ↓とか。
Go Modules でインターネット上のレポジトリにはないローカルパッケージを import する方法
なんやねんインターネット上にパッケージがあるのが前提て。これ使うな?デファクトスタンダード技術使うなとか、それこそ初学者殺しでしょ……。NameSpace & Classに慣れきっているのでどうすりゃいいんだこれ?ってのがある。goはClassの概念がないので初めは細かくパッケージ切ってやっていこうかと思ったのだけど、この仕様のせいでダルすぎてなー。どうするのが最適解なのか。まーそのうちわかんだろ。
おわり。

修正しました。てへぺろ

というわけでgolangの勉強を再開したのだが、

package main

import (
    "fmt"
)

func main() {
    fmt.Println("hello")
    fmt.Println("Raspberry Piでも動くかな?Windowsも!")
}
$Env:GOOS = "linux"; $Env:GOARCH = "arm"; $Env:GOARM=6; go build -o hello_rp hello.go

golang

明日からバリバリ書くけど眠いから寝る。とベッドに入って思った。ナチュラルにARMでビルドしたけど、そもそもラズパイってARMじゃん。なんで.net coreでlinux-x86とか言ってんの?!?!で、試した。

using System;

namespace RpTest {
    class Program {
        static void Main(string[] args) {
            Console.WriteLine("Hello .net core 3.1!");
            Console.WriteLine("System.Environment.Version : " + System.Environment.Version);
            Console.Read();
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121. 
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <Configuration>Release</Configuration>
    <Platform>Any CPU</Platform>
    <PublishDir>bin\Release\netcoreapp3.1\publish\</PublishDir>
    <PublishProtocol>FileSystem</PublishProtocol>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <RuntimeIdentifier>linux-arm</RuntimeIdentifier>
    <SelfContained>true</SelfContained>
    <PublishSingleFile>True</PublishSingleFile>
    <PublishTrimmed>False</PublishTrimmed>
  </PropertyGroup>
</Project>

ARM32がない……?(ちなみに手書きで強制的にarm32に書き換えてもエラー) OS自体に.net coreをインストールする方法は腐るほど出てくるのだけど、クロスコンパイルに関してはARMの情報は探さないとないかー。

言い訳させていただくと、前回の記事にあるようにラズパイ関連のプログラミングエントリを検索すると、ぱいそんだらけだったので「公式以外見るの止めた!」となり、なんか変な誤解というか脳がバグって先のエントリみたいな勘違いをしてしまった模様。酒飲んでないのに。むしろ酔拳効果がないとも言える。(断酒開始から4週経過)

追記

こいつかー

Note: All models of generation 1 and Pi Zero are not supported because the .NET Core JIT depends on armv7 instructions not available on those versions.

はじめよう!golang

はじめよう!つーかとっととやれや(俺自身に向けて)的な。
かなり前から他の言語を学ぼうかと思っていた(*1)のだが、結局golangにすることにした。静的型付けが良いとか、バッチとかでパフォーマンス出せそうとか、nginxで投げ込むの楽そうとか、そんなところで。c#が好きなので最後まで.net coreと迷った(*2)のだけど、環境構築がかったるそうなので。スターティングGO言語という書籍を購入してさらっと読んだのだが、当たり本だったようで容易く理解できた。あとはガリガリ用もないのに書きまくるのみ。hello worldとかやってもしょうがないので、この糞ブログのGO版ミラーを週末に作りたい。正直言うとwebでCMSだとかなんだとかを作るだけならPHP(7.x) & opcacheで全く問題ない気がしているけどな。糞システムのボトルネックはDBの糞設計で9割決まるよトホホ。

*1
わしの言語歴(正直今書けって言われてもc#以前は無理)
f-basic386, c, c++(思い出したくもないMFC), delphi(object pascal), c#, php, オマケ:javascript

*2
全部やれ?甥と遊んだりビール飲んだりゲームやったりするほうが大事なので……。時間は有限だわ。