アナログCPU:5108843109

ゲームと音楽とプログラミング(酒と女とロックンロールのノリで)

('ω') < イザユケエンジニャー

駅メモ:でんこ育成状況とコンプ進捗状況

随時更新します。

都道府県コンプ状況

凸・カンストグランクラスの記録(記載は達成順)

  • 上限開放完了
    • ニャッシュ
    • りんご
    • コタン
    • (もえ)
    • いおり
    • ルナ
    • ひいる
    • (れいこ)
    • るる
    • レン
    • レーノ
    • いちほ
    • (いずな)
    • そら
    • Liko
    • セリア
    • (ありす)
    • ミオ
    • ほこね
    • みこと
    • みらい
    • イムラ
    • レイカ
    • (ふぶ)
    • みろく
    • メロ
    • (アサ)
    • うらら
    • しぐれ
    • さや
    • (ペルリ)
    • リオナ
    • エリア
    • しいら
    • まりか
    • にころ
  • カンスト
    • (れいこ)
    • いずな
    • (もえ)
    • (ふぶ)
    • ありす
    • (アサ)
    • (ペルリ)
  • グランクラス
    • れいこ
    • もえ
    • ふぶ
    • アサ
    • ペルリ

駅メモ #16:3人目のカンスト

もえさんもカンスト

f:id:honey8823:20161130172803p:plain:w400

進捗:前回(10/31)からの比較

  • 駅 2935 → 2945(+10)
  • 路線 371 → 371(+0)
  • 都道府県 30 → 30(+0)
  • コンプ路線 170 → 171(+1)

無事にハッカドールイベントの250駅を突破したので2号さんをお迎えし、
2周年ガチャであたる・なほも確保。
現時点で未所持はなつめだけになりました。
12月の新でんこはまたそろそろアタッカーかな?

育成進捗(今回から変化のあったのだけ残すことにする) ()内は前回比 ☆は凸完了、★はカンスト
[S]セリア 8/80 (上限5up) ☆
[S]もえ 80/80 (Lv9up) ★
[D]しいら 11/75 (上限5up)
[A]イムラ 50/70 (上限5up)
[S]しぐれ 16/70 (Lv9up / 上限5up)
[S]うらら 47/65 (Lv16up / 上限5up)
[T]チコ 18/60 (上限5up)
[A]レーノ 8/80 (Lv1up) ☆
[D]ありす 72/75 (Lv2up)
[S]ミオ 6/70 (上限5up)
[S]みこと 23/75 (上限10up)
[D]くに 6/60 (上限5up)
[D]にちな 9/70 (上限10up)
[A]そら 8/80 (上限5up) ☆
[S]アサ 70/70 (Lv5up)
[A]さいか 13/60 (上限5up)
[S]カノン 1/55 (上限5up)
[A]あたる 1/50 (new!)
[S]スピカ 21/55 (Lv20up / 上限5up)
[D]なほ 1/50 (new!)
[A]Liko 6/80 (上限5up) ☆
[T]ハッカ1号 1/50 (new!)
[T]ハッカ2号 1/50 (new!)
[T]ハッカ3号 1/55 (上限5up)

未所持
・なつめ

凸&カンスト状況
駅メモの育成状況 - アナログCPU:5108843109


自分用育成メモ:

相変わらずサポーター育成強化中。サポーターじゃないけどいろはも加えよう。
もえさんが無事カンストしたので、優先度としては
 ふぶ>アサ>うらら>スピカ>いろは>しぐれ>みこと>カノン>レイカ>ひいる>セリア>ミオ
という感じかな。
ふぶ・アサは仮カンスト中なので(ただし上限突破次第最優先で育てる)
当面のリンクスコア狙いは うらら>いろは>みこと>スピカ
新駅ボーナス狙いは スピカ>いろは>しぐれ>みこと
でいこう。ひとまずLv30,Lv50を目処にという感じで。


そしてやっぱりマイルや経験値に影響の出にくいところではれいこさんの回数稼ぎ継続中。

整数かどうか、またその範囲をチェックする関数

整数かどうかを一発チェックしてくれる関数を探していたのですが見つからず。
…えっ、本当にない…? いやあるでしょ…?
しかし見つからなかったので、とりあえず作りました。誰か知ってたら教えてください。

せっかくなので範囲チェックもやってくれるオマケ付きです。

で、「範囲チェックが不要なときもあるので、引数の最小値・最大値が省略されたかどうか」を調べる方法を探し…
IsMissingという関数で調べることは可能というのは分かったのですが、引数がVariantでなければならない様子。うーむ…

参考
VB6・VBA関数メモ:IsMissing関数 | フィロの村note

関数

Private Function checkLong(ByVal vNum As Variant, Optional ByVal lMinNum, Optional ByVal lMaxNum)

    If CLng(vNum) <> vNum Then
        ' 引数が整数でない場合FALSE
        checkLong = False
        Exit Function
    End If
    
    If IsMissing(lMinNum) = False Then
        ' 最小値が指定されている場合
        If vNum < lMinNum Then
            ' 引数が最小値を下回っている場合FALSE
            checkLong = False
            Exit Function
        End If
    End If

    If IsMissing(lMaxNum) = False Then
        ' 最大値が指定されている場合
        If lMaxNum < vNum Then
            ' 引数が最大値を上回っている場合FALSE
            checkLong = False
            Exit Function
        End If
    End If

    checkLong = True

End Function

使用例

Debug.Print checkLong(5)       ' True :整数
Debug.Print checkLong(3.1)     ' False:整数でない
Debug.Print checkLong(5, 1, 9) ' True :整数(1~9の範囲内)
Debug.Print checkLong(5, 6, 9) ' False:整数(6~9の範囲外)

ブックを開くとき/閉じるときの処理

普通どおり標準モジュールにマクロを作るとき、プロシージャ名を
「Auto_Open」にすればブックを開いたとき、
「Auto_Close」にすれば閉じるときに
そのプロシージャが起動します。

「ブックを開いたら今日のおみくじを表示」みたいなお遊びから
「閉じる前に設定内容等を強制保存」まで色々と使えそうです。

尚、イベントプロシージャを使用する方法もあります。
詳細はこちら。
エクセルExcel大事典 VBAマクロ イベントプロシージャ Open Target Cancel EnableEvents Volatile

これだけではなんなのでサンプルを。
ブックを開閉するタイミングで特定のシートを非表示にするサンプルです。

Option Explicit

Const SHEETNAME As String = "samplesheet" 'シート名

'*
'* ファイルオープン時の自動実行
'*
Public Sub Auto_Open()
    ' シートを非表示に(VeryHiddenならExcel画面での再表示は不可)
    ThisWorkbook.Sheets(SHEETNAME).Visible = xlSheetVeryHidden
   
End Sub

'*
'* ファイルクローズ時の自動実行
'*
Public Sub Auto_Close()
    ' シートを非表示に
    ThisWorkbook.Sheets(SHEETNAME).Visible = xlSheetVeryHidden
End Sub

301 VS 302

開発中のサイトで、https://でアクセスされたらhttp://にリダイレクトしたり
その逆をやったり、という事案があり、リダイレクトについて諸々調べてたので残しておきます。

HTTPステータスコード

リダイレクトをかます時に、HTTPステータスコードを乗っけることができるらしい。
ほほう。今回のケースだと何か特別乗っけないといけないのかしら?

HTTPステータスコードっていうと、
「403 Forbidden」「404 Not Found」とかいうアレ。
とりあえずいろいろ調べてみる。

参考
HTTPステータスコード - Wikipedia
よく見るHTTPステータスコード一覧とその意味を理解する | SEO HACKS公式ブログ

ざっと見た感じ、301か302かな…?
と思って検索してみたら、案の定「301vs302」みたいな記事がごろごろ。

参考
SEOを正しく行う為に301リダイレクトや302リダイレクトを理解する | SEO Imagination!ブログ
SEO的に301リダイレクトは是が非か? | ランサーズ社長日記
301リダイレクトと302リダイレクト、meta refreshの設定方法や使い方 | WP SEOブログ
Google、モバイル向けサイトのリダイレクトには301よりも302を使うように推奨を変更 | 海外SEO情報ブログ
chromeは、301リダイレクトをキャッシュしている - too_youngの日記

いろいろ貼りましたが、まとめると…

  • 301
    • 引越しなど、その後ずっと移動先URLを使用する場合に使用
    • 「○○.jp/hoge」から「○○.jp/hoge/」に移動させる際もこちら
    • googleからの評価などが移動先URLに引き継がれる
      • 評価やアクセスの一本化
      • 移動元URLは検索結果に出なくなる?
    • 301に設定していても200が返る現象もあるらしい?
    • 環境によっては移動先URLに直接アクセスするようキャッシュされる
  • 302
    • メンテ中など、一時的に移動先URLに移動させたい場合に使用
    • googleからの評価などの移動は発生しない
      • 移動元も移動先も検索結果に出る(が評価は分散する)?
    • ただし、302が何度も検知されるサイトについては301として扱われる場合もある
    • 「○○.jp/index.html」のサイトを「○○.jp」として検索結果に出すのにも有効
      • 「○○.jp」でアクセスしてもらって「○○.jp/index.html」に遷移させる
    • モバイル端末でPCサイトにアクセスした際のモバイルページへのリダイレクトは302推奨
    • あくまで一時的なものなのでキャッシュはされない

という感じですかね。

実際の挙動は以下のような感じだそうです。

301リダイレクトと302リダイレクトの違い | 海外SEO情報ブログ

うーん、今回の場合、httpをhttpsに転送したりその逆だったり…
どっちのURLも存在するから引っ越すわけではないし、
かといって一時的な移動でもないし、
っつーかその微妙な違いが上記のパターンにちゃんと当てはまるんかいな…
要するにどちらを使えば…。

…と迷いましたが、301を採用することにしました。
決め手にしたのは、
301の場合→「○○.jp/hoge」から「○○.jp/hoge/」への移動
302の場合→「○○.jp」から「○○.jp/index.html」への移動
というところです。
移動元を検索エンジンに残したいか残したくないかですね。


そして実際にHTTPステータスコードを乗っけたいとき、PHPではhttp_response_codeを使用すればOK。

PHP: http_response_code - Manual

シートの存在を調べて存在しなければ新しいシートを作成

頻繁に使うロジックなので関数化してみました。

  • 任意の名称を持つシートが存在するかどうか調べる
  • 上記で存在しなければその名称で作成(もしくはあるシートをコピーしてその名称にする)
    ' 呼び出し例(1)
    Dim wsSheet As Worksheet
    Set wsSheet = SelectSheet("シートA", "テンプレート")
    ' → 常に wsSheet = ThisWorkbook.Sheets("シートA") となる。
    '    ただし、「シートA」が存在すればそのシート、
    '    存在せず、「テンプレート」シートが存在すればそれをコピーしたもの、
    '    「テンプレート」シートも存在しない場合は新規シート。

    ' 呼び出し例(2)
    Dim wsSheet As Worksheet
    Set wsSheet = SelectSheet("シートA")
    ' → 常に wsSheet = ThisWorkbook.Sheets("シートA") となる。
    '    ただし、「シートA」が存在すればそのシート、
    '    存在しない場合は新規シート。

'*
'* シートがなければ作成orコピーする関数
'* (有効なsBaseSheetが指定された場合、そのシートをコピーして名称をsSheetNameとする)
'*
'* [in]  sSheetName:取得するシートの名称
'*       sBaseSheet:コピー元 ※省略可
'* [out] SelectSheet:取得したシート
'*
'* 使用関数:CheckExistSheet
'*
Private Function SelectSheet(ByVal sSheetName As String, Optional ByVal sBaseSheetName As String = "") As Worksheet

    ' 変数
    Dim wsSheet As Worksheet
    Dim bFlag As Boolean
    
    ' シートの存在を調べ、なければ作る
    If CheckExistSheet(sSheetName) <> True Then
        ' 存在しないので、作成orコピーする
        If sBaseSheetName = "" Or CheckExistSheet(sBaseSheetName) = False Then
            ' コピー元シートを指定されていないor無効なシートなので新規作成
            ThisWorkbook.Sheets.Add After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)
        Else
            ' コピー元シートをコピーして名称変更
            ' ※Copyの引数はコピーした後の位置。After:=(Before:=)を用いて指定する
            With ThisWorkbook.Sheets(sBaseSheetName).Copy(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
                ActiveSheet.Name = sSheetName
            End With
        End If
    End If
    
    '指定された名称のシートを返す
    Set SelectSheet = ThisWorkbook.Sheets(sSheetName)
    
End Function

'*
'* シートが存在するか調べる関数
'*
'* [in]  sSheetName:存在を調べるシート名
'* [out] CheckExistSheet:結果(存在すればTrue、しなければFalse)
'*
Private Function CheckExistSheet(ByVal sSheetName As String) As Boolean

    ' 変数
    Dim wsSheet As Worksheet
    
    ' 戻り値のデフォルト値をFalseに
    CheckExistSheet = False
    
    For Each wsSheet In Worksheets
        If wsSheet.Name = sSheetName Then
            ' シートが存在すれば戻り値をTrueにしてループから抜ける
            CheckExistSheet = True
            Exit For
        End If
    Next wsSheet
    
End Function

日時を扱ういろいろ PHP版

PHPでの日時の扱いについてですが、INT型のUnixタイムスタンプを用いるのが基本です。

例えば現在の日時を「YYYY-MM-DD hh:mm:ss」という形で求める場合、

$datetime = date("Y-m-d H:i:s", time())

とします。
time()で現在のUnixタイムスタンプを取得し、それを指定のフォーマットに変換しています。

フォーマットした日付文字列をつくる

date(フォーマット,Unixタイムスタンプ)を使用。
上記の説明のとおりですが、現在日時の場合はUnixタイムスタンプの指定を省略してもOKです。
フォーマットのパラメータに使用できる文字はマニュアルの通りですが、
個人的によく使用するものをこちらに挙げておきます。

Y 年(西暦4桁)
y 年(西暦下2桁)
m 月(先頭ゼロ埋めする)
n 月(先頭ゼロ埋めしない)
d 日(先頭ゼロ埋めする)
j 日(先頭ゼロ埋めしない)
N 曜日(1:月曜 2:火曜 … 7:日曜)
w 曜日(0:日曜 1:月曜 … 6:土曜)
t 該当する月の日数(=末日)
H 時(24時間方式)
i
s

「t」が意外と便利。末日を取りたければ「Y-m-t」のフォーマットでOK。便利。

各要素をバラバラに取りたい場合であればgetdateが使いやすい場面もあるかも。

また、月だけ・日だけ…などを文字列ではなく数値として取りたい場合にはidateなんて関数もあります。
どうしても型を意識しなければならない場合には使える…かも?
(見慣れない関数入れるよりも、dateで取ってキャストした方が可読性高い気がしますが…)

参考
PHP: getdate - Manual
PHP: idate - Manual

日付からUnixタイムスタンプを取得する(1)

mktime(時, 分, 秒, 月, 日, 年, サマータイムかどうか) を使用するパターン。
引数は右から順に省略可能で、すべて省略した場合は現在時刻となります。
ただし、現在時刻を求める場合はtime()を使用すべきです。
(PHP5.1以降では、引数をすべて省略した場合noticeが出るようです)

引数はすべて整数で渡します。
範囲外の数字はPHPで判断して処理してくれます。
例えば月に13を指定すると繰り越して「1月」としてくれたり、
月に1、日に0を指定すると「12月31日」としてくれたりします。

// 例
$date = date("Y-m-d", mktime(0, 0, 0, 11, 31, 2013)); // 2013-12-01

日付からUnixタイムスタンプを取得する(2)

strtotime(日付文字列) を使用するパターン。
日付文字列には色々なものを入れられます。

// 例
$timestamp = strtotime("now");               // time()で充分ですが…
$timestamp = strtotime("2013-11-01");        // 普通の日付
$timestamp = strtotime("01 November 2013");  // 海外式
$timestamp = strtotime("-1 day");            // 昨日の日付
$timestamp = strtotime("2013-11-01 -1 day"); // 2013/11/01の前日
$timestamp = strtotime("+1 week");           // 1週間後
$timestamp = strtotime("+1 month");          // 1ヶ月後
$timestamp = strtotime("+1 year");           // 1年後
$timestamp = strtotime("+1 hour");           // 1時間後
$timestamp = strtotime("+1 minute");         // 1分後
$timestamp = strtotime("+1 second");         // 1秒後
$timestamp = strtotime("next Monday");       // 次の月曜日
$timestamp = strtotime("last Sunday");       // 前の日曜日

…何でもアリですね。これがPHPの楽しいところでも便利なところでも怖いところでもあるというか…

「1日後」「1時間後」ならともかく、「1ヶ月後」なんかは意図した日付になるとは限らないので注意が必要。

参考
d.hatena.ne.jp

日付が有効かどうかを調べる

checkdate(月, 日, 年) を使用。
有効な場合はTRUE、無効な場合はFALSEが返ってくる。

// 例
$result = checkdate(12,31,2013)  // true
$result = checkdate(12,32,2013)  // false

日時を扱ういろいろ MySQL版

MySQLを前提として書いています

現在日時の取得

-- YYYY-MM-DD hh:mm:ss
SELECT NOW()

月の最終日の取得

-- 2013-06-10 を渡すと 2013-06-30
SELECT LAST_DAY(日付)

フォーマットして取得

SELECT DATE_FORMAT(日付, フォーマット)

フォーマット部分

  • %Y 西暦年(4桁)
  • %y 西暦年(2桁)
  • %m 月(2桁ゼロ詰め)
  • %c 月(ゼロ詰めなし)
  • %d 日(2桁ゼロ詰め)
  • %j 日(ゼロ詰めなし)
  • %W 曜日(英語)
  • %w 曜日(日:0、月:1、…、土:6)
  • %k 時(24時間表記)
  • %i 分
  • %s 秒

フォーマット例:「2013/06/09」→ %Y/%m/%d

ここまで覚えておけば組み合わせで大体のことはできそうです

まあ覚えられないので書いてるんですけどね。

以下は蛇足。

現在日時の取得

SELECT
  current_timestamp -- YYYY-MM-DD hh:mm:ss
 ,current_date      -- YYYY-MM-DD
 ,CURDATE()         -- YYYY-MM-DD
 ,current_time      -- hh:mm:ss
 ,CURTIME()         -- hh:mm:ss

曜日の取得

SELECT
  WEEKDAY(日付)   -- 月:0、火:1、…、日:6
 ,DAYOFWEEK(日付) -- 日:1、月:2、…、土:7

これに加え、フォーマット指定の「%w」が「日:0、月:1、…、土:6」と
すべて戻り値が異なるので、
好みの方法で統一しておく方がリスクが少ない。
尚、PHPでのフォーマットにも

  • 月:1、火:2、…、日:7
  • 日:0、月:1、…、土:6

と2通りあり、合わせる必要がある場合はフォーマット指定を利用するとよい。

すごくまぎらわしい。

マルチバイト文字列関数の罠

ある時、CSVのデータをインポートする処理で不具合が発生。
「2件のデータをまとめてインポートすると1件のみ失敗して、
成功したデータを除いて再度その1件のみをインポートすると成功した」
いやいやそんなアホな。

色々調べた結果、この結論に至りました。
「マルチバイト文字列関数を使用するときは文字コードを指定した方が安全かもしれない」

参考
d.hatena.ne.jp


…うーん…。
今でも微妙に納得いかないのですが。

ちなみに、当該箇所の処理はこうです。

  • CSVを取り込む
  • 改行区切りで1セットのデータに分割
  • カンマ区切りでデータ内の各項目に分割して文字配列にする
  • ある項目を全角数字から半角数字に変換(mb_convert_kanaを使用)
  • 他項目も含めいろいろ調整したデータをDBに入れたりいろいろする

…という感じなのですが、どうやら全角→半角にする過程で誤変換されたようです。
有効な半角数字にならず、データ不備としてはじかれた、と。
何故か、その2件のデータを一緒に取り込んだ時のみ、片方だけ。
なので特定の文字列が誤変換されるというわけではないですし、問題なく複数レコードが取り込めることだってありました。
(開発時にも、もちろんテストしましたし…)

ともかく、
>全角数字から半角数字に変換(mb_convert_kanaを使用)
これを

$hoge = mb_convert_kana($hoge, "a")

と書いていたのを

$hoge = mb_convert_kana($hoge, "a", "UTF-8")

こうすると改善されました。

誤変換しない場合の方が圧倒的に多いというのがネックですね…。
テストで気付きにくいですし。

他のマルチバイト系関数を使用する際も、文字コードが指定できるものはした方が安全かもしれません。


関係ありそうでしかもちょくちょく使う関数一覧
(リンクは公式マニュアル)

文字列のトリム(左端・右端にある特定の文字列を削除 )

以下MySQLで確認していますが、OraclePostgreSQLでも同じ書き方である模様。

例えば都道府県欄に「○○県」と入っており「県」は除いて表示したい場合など、は以下のようにします。

SELECT TRIM(オプション 削除する文字列 FROM フィールド名)
FROM テーブル名

オプションは、
両端から削除:BOTH (※省略可)
左端から削除:LEADING
右端から削除:TRAILING
となります。

-- 「○○県」から右端の「県」を取り除いて表示
SELECT TRIM(TRAILING '県' FROM `pref`)
FROM `pref_master`