ベスパリブ

ベスパもってないです。バイク買いました。

SendGridのメールで改行コードが反映されない問題

SendGridでメールを送信するとき、本文に改行コードを入れても、メール受信時には本文が改行がされなくて困りました。

ダメ元でググったらまんまのトラブルシューティングが公式から出てました。

sendgrid.kke.co.jp

PlainContentをActiveにすると症状は直りました。やったね。

Cloud Functionsのダイレクトトリガーでbase64エンコードしたデータを送信して実行する

GCPのCloud Functionsには動作テストをするためにダイレクトトリガーというものがあります。このダイレクトトリガーをする方法にGUIのテストを実行するか、CUIでcallコマンドを実行する方法があります。

詳しくは以下の2つのリンクに書いてあります。

Direct Triggers  |  Cloud Functions Documentation  |  Google Cloud

https://cloud.google.com/sdk/gcloud/reference/beta/functions/call

1個目のリンクにhelloPubSubのサンプルコードがあるのですが、これに送るデータはbase64エンコードする必要があります。

以下はサンプルコードそのままのコピペです。

/**
 * Background Cloud Function to be triggered by Pub/Sub.
 * This function is exported by index.js, and executed when
 * the trigger topic receives a message.
 *
 * @param {object} event The Cloud Functions event.
 * @param {function} callback The callback function.
 */
exports.helloPubSub = (event, callback) => {
  const pubsubMessage = event.data;
  const name = pubsubMessage.data
    ? Buffer.from(pubsubMessage.data, 'base64').toString()
    : 'World';

  console.log(`Hello, ${name}!`);

  callback();
};

上記のhelloPubSubに'PubSub!'という文字列を送信して実行するコマンドは以下になります(サンプルそのまま)。

DATA=$(printf 'PubSub!'|base64) && gcloud functions call helloPubSub --data '{"data":"'$DATA'"}'

上記のような'PubSub!'のような短い文字列はこのままでいけるのですが、base64コマンドは長い文字列をエンコードしようとする場合、なぜか半角スペースが入るようです。

例えば以下のようなデータをbase64エンコードすると、

DATA=$(printf '{"a" : "1547604173", "b" : "-0.91", "c" : "0.40", "d" : "10.45", "e" : "80.58", "f" : "1013.67", "g" : "36.98", "h" : "", "i" : "", "j" : "0.00"}'|base64)
echo -n $DATA

実行結果は以下。

eyJhIiA6ICIxNTQ3NjA0MTczIiwgImIiIDogIi0wLjkxIiwgImMiIDogIjAuNDAiLCAiZCIgOiAi MTAuNDUiLCAiZSIgOiAiODAuNTgiLCAiZiIgOiAiMTAxMy42NyIsICJnIiA6ICIzNi45OCIsICJo IiA6ICIiLCAiaSIgOiAiIiwgImoiIDogIjAuMDAifQ==

このようにbase64コマンドは見栄えを整えて半角スペースを入れてくれます。余計なことを。

この場合はしょうがないのでtrコマンドで半角スペースとついでに改行コードを除去します。

DATA=$(printf '{"a" : "1547604173", "b" : "-0.91", "c" : "0.40", "d" : "10.45", "e" : "80.58", "f" : "1013.67", "g" : "36.98", "h" : "", "i" : "", "j" : "0.00"}'|base64|tr -d " \n")
echo $DATA

実行結果は以下。

eyJhIiA6ICIxNTQ3NjA0MTczIiwgImIiIDogIi0wLjkxIiwgImMiIDogIjAuNDAiLCAiZCIgOiAiMTAuNDUiLCAiZSIgOiAiODAuNTgiLCAiZiIgOiAiMTAxMy42NyIsICJnIiA6ICIzNi45OCIsICJoIiA6ICIiLCAiaSIgOiAiIiwgImoiIDogIjAuMDAifQ==

これでよし。

例えばJSONデータを送信したい場合、以下のようなシェルスクリプトを用意しコンソールで実行してやれば、helloPubSubを実行することができます。

DATA=$(printf '{"a" : "1547604173", "b" : "-0.91", "c" : "0.40", "d" : "10.45", "e" : "80.58", "f" : "1013.67", "g" : "36.98", "h" : "", "i" : "", "j" : "0.00"}'|base64|tr -d " \n")
gcloud functions call helloPubSub --data '{"data":"'$DATA'"}'

Cloud FunctionsのSendGridのチュートリアルの備忘録

GCP(Google Cloud Platform)のCloud FunctionsのSendGridのチュートリアルをしました。

cloud.google.com

ちなみにCloud Functionsのチュートリアルのリンク↓

チュートリアル  |  Cloud Functions  |  Google Cloud

チュートリアルになかなか手こずったので備忘録です。

以下の章はチュートリアルの章のタイトルと同じです。

始める前に

書いてあるとおりにします。

データの流れを可視化

読みます。なるほど。

アプリケーションの準備

書いてあるとおりに進めます。 SendGridのAPIキーは一度しか表示されないので、どこかにコピペしておきます。

https://[YOUR_USERNAME]:[YOUR_PASSWORD]@[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/sendgridWebhook
  • [YOUR_USERNAME]と[YOUR_PASSWORD]は任意のユーザ名とパスワードです。適当にわかりやすいのにしてよいです。

  • [YOUR_PROJECT_ID]はCloud プロジェクトIDで、GCPのホームの「ダッシュボード」メニューの「プロジェクト情報」に記載されている「プロジェクトID」です。

  • [YOUR_REGION]は関数がデプロイされる領域らしいです。どう選べば良いのかわかりませんが、GCEのVMインスタンスと同じリージョンにしました。リージョンの確認は、GCEのページに行ってVMインスタンスの詳細に「ゾーン」の項目があるのですが、それを見ればわかります。ゾーンが「us-central1-c」なら、リージョンは「us-central1」です。

注意としては、「SELECT ACTIONS」の項目にチェックを入れないと、SendGridがメールを送信するときにWebhookイベントが発生しません。これチュートリアルに記述がまんま抜けているので注意(2019/01/11現在)。選択するACTIONはおそらく「Processed」と「Delivered」だけで良いが、チュートリアルなので「ALL」で良い。

f:id:takeg:20190111111250p:plain
SELECT ACTIONSの有効化も忘れずにする

こっちの日本語ドキュメントのほうが正確に書いてあります。

Event Webhookでイベントを受信する - ドキュメント | SendGrid

そのほかは書いてあるとおりに進めます。

コードを理解する

コードの説明がざっくり書いてあります。

関数のデプロイ

ここも書いてあるとおりに進めます。

$ gcloud beta functions deploy sendgridEmail --trigger-http
$ gcloud beta functions deploy sendgridWebhook --trigger-http
$ gcloud beta functions deploy sendgridLoad --trigger-bucket [YOUR_EVENT_BUCKET_NAME]

[YOUR_STAGING_BUCKET_NAME]とか書いてありますが、別に使いません。何かの間違いでしょう。

メールを送信

$ curl -X POST "https://[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/sendgridEmail?sg_key=[YOUR_SENDGRID_KEY]" --data '{"to":"[YOUR_SENDER_ADDR]","from":"[YOUR_RECIPIENT_ADDR]","subject":"Hello from Sendgrid!","body":"Hello World!"}' --header "Content-Type: application/json"
  • [YOUR_SENDER_ADDR]は送信者のアドレスです。よって"from"の方です(チュートリアルが間違っている)。こっちにSendGridアカウントのメールアドレスを書きます。

  • [YOUR_RECIPIENT_ADDR]は受信者のアドレスです。メールの受信を確認できるアドレスを入れます。

たとえば次のような感じになります。

$ curl -X POST "https://us-central1.my_project/sendgridEmail?sg_key=hogehogehogehogehoge" --data '{"to":"to@example.com","from":"from@example.com","subject":"Hello from Sendgrid!","body":"Hello World!"}' --header "Content-Type: application/json"

これを実行すると、以下のエラーが発生しました。

curl: (51) SSL: no alternative certificate subject name matches target host name 'us-central1.my_project.cloudfunctions.net'

このエラーメッセージでググると、Stack Overflowがヒットします。

api - Fix CURL (51) SSL error: no alternative certificate subject name matches - Stack Overflow

-kオプションでSSL証明書の検証をオフにできるそうです。とりあえずそうしてみます。

$ curl -kX POST "https://us-central1.my_project/sendgridEmail?sg_key=hogehogehogehogehoge" --data '{"to":"to@example.com","from":"from@example.com","subject":"Hello from Sendgrid!","body":"Hello World!"}' --header "Content-Type: application/json"

これを実行すると、今度は以下の結果が返ってきました。

<!DOCTYPE html>
<html lang=en>
  <meta charset=utf-8>
  <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
  <title>Error 404 (Page not found)!!1</title>
  ...

Error 404!! ページが存在しないと言われました。なんで?

結果から言うと、URLのタイポでした。タイポには気をつけましょう。

Cloud FunctionsのHTTPトリガーのURLの確認は、Cloud Functionsのページに行けばデプロイした関数がリストされているので該当の関数をクリックし(今回はsendgridEmail)、「トリガー」メニューの画面で確認することができます。

f:id:takeg:20190110190658p:plain
sendgridEmail関数のURLの確認

さて、タイポを修正して再度実行すると、今度はSendGridからのメールを受信することができました。やったね。

Cloud Functionsの実行ログを見てみます。

$ gcloud beta functions logs read --limit 100

....
D      sendgridEmail  vra---gkc     2019-01-10 18:09:22.766  Function execution started
I      sendgridEmail  vra---gkc     2019-01-10 18:09:22.773  Sending email to: to@example.com
I      sendgridEmail  vra---gkc     2019-01-10 18:09:23.120  Email sent to: to@example.com
D      sendgridEmail  vra---gkc     2019-01-10 18:09:23.122  Function execution took 356 ms, finished with status code: 202

sendgridEmailが実行されたあと、SendGridがメールを送信してくれます。そのときにSendGrid側のWebhookイベントが同時に発生し、 https://[YOUR_USERNAME]:[YOUR_PASSWORD]@[YOUR_REGION].[YOUR_PROJECT_ID].cloudfunctions.net/sendgridWebhook宛にPOSTを送信します。POSTを受信するとGCP側のCloud FunctionsのsendgridWebhookが実行されます。するとGoogle Cloud StrageにJSONファイルが保存されます。

f:id:takeg:20190111112837p:plain
Google Cloud StrageにJSONファイルが保存される

sendgridWebhookの実行ログは以下のようになるはずです。

D      sendgridWebhook  n64---gwu     2019-01-11 02:05:42.699  Function execution started
I      sendgridWebhook  n64---gwu     2019-01-11 02:05:42.707  Saving events to 1547172342706000-6630d5c0-d6f8-40d6-a630-9a844e4239b3.json in bucket gcf_sendgrid_tutorial_bucket
I      sendgridWebhook  n64---gwu     2019-01-11 02:05:42.999  JSON written to 1547172342706000-6630d5c0-d6f8-40d6-a630-9a844e4239b3.json
D      sendgridWebhook  n64---gwu     2019-01-11 02:05:43.001  Function execution took 302 ms, finished with status code: 200

もしログがいつまで経っても現れない場合、SendGrid側のWebhookが発生していない可能性があります。「アプリケーションの準備」の章を見直してみてください。

sendgridLoadのエラー

ここまでやったら、sendgridEmailからsendgridWebhookまでは実行が完了するのですが、sendgridLoadで以下のエラーが出ました。

D      sendgridLoad     353717043065300  2019-01-11 11:29:34.810  Function execution started
I      sendgridLoad     353717043065300  2019-01-11 11:29:35.217  Starting job for 1547206173966000-13e6adf5-271c-42c5-bac3-116d89ef3ea6.json
I      sendgridLoad     353717043065300  2019-01-11 11:29:35.218  Job failed for 1547206173966000-13e6adf5-271c-42c5-bac3-116d89ef3ea6.json
E      sendgridLoad     353717043065300  2019-01-11 11:29:35.241  TypeError: table.import is not a function
                                                                      at Promise.resolve.then.then (/user_code/index.js:340:26)
                                                                      at process._tickDomainCallback (internal/process/next_tick.js:135:7)
D      sendgridLoad     353717043065300  2019-01-11 11:29:35.251  Function execution took 442 ms, finished with status: 'error'

TypeError: table.import is not a function

tableにはimportという関数がないと言われました。

あまり良くわかっていないのですが、 @google-cloud/bigquery 2.0.5 » Class: Table  |  Node.js  |  Google Cloud を見ると、importという関数はないようです。loadの間違い?

なんか色々こねこねして最終的にできたsendgridLoadのコードが以下。自分でも何をやっているのかよくわかっていない。

// [START functions_sendgrid_load]
/**
 * Cloud Function triggered by Cloud Storage when a file is uploaded.
 *
 * @param {object} event The Cloud Functions event.
 * @param {object} event.data A Cloud Storage file object.
 * @param {string} event.data.bucket Name of the Cloud Storage bucket.
 * @param {string} event.data.name Name of the file.
 * @param {string} [event.data.timeDeleted] Time the file was deleted if this is a deletion event.
 * @see https://cloud.google.com/storage/docs/json_api/v1/objects#resource
 */
exports.sendgridLoad = event => {
  const file = event.data;

  if (file.resourceState === 'not_exists') {
    // This was a deletion event, we don't want to process this
    return;
  }

  return Promise.resolve()
    .then(() => {
      if (!file.bucket) {
        throw new Error(
          'Bucket not provided. Make sure you have a "bucket" property in your request'
        );
      } else if (!file.name) {
        throw new Error(
          'Filename not provided. Make sure you have a "name" property in your request'
        );
      }

      return getTable();
    })
    .then(([table]) => {
      const fileObj = storage.bucket(file.bucket).file(file.name);
      console.log(`Starting job for ${file.name}`);
      const metadata = {
        autodetect: true,
        sourceFormat: 'NEWLINE_DELIMITED_JSON',
      };
      // tableのプロパティを全て出力させる
      for(var n in table){
        console.log("table: " + n);
      }
      // Error: table.import is not function ... why?
      console.log("table has import: " + ("import" in table)); 

      //return table.import(fileObj, metadata);
      return table.load(fileObj, metadata);
    })
    //.then(([job]) => job.promise())
    .then(([job]) => {
      for(var n in job){
            // jobのプロパティを全て出力させる
        console.log("job: " + n);
      }
    })
    .then(() => console.log(`Job complete for ${file.name}`))
    .catch(err => {
      console.log(`Job failed for ${file.name}`);
      return Promise.reject(err);
    });
};
// [END functions_sendgrid_load]

以下はその実行結果

D      sendgridLoad     357398789515366  2019-01-15 04:02:31.061  Function execution started
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.245  Starting job for 1547524950091000-396529d3-8fad-4bcb-b527-f1c24a3bb25b.json
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: domain
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: _events
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: _eventsCount
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: _maxListeners
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: metadata
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: baseUrl
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: parent
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: id
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: createMethod
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: methods
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: interceptors
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: Promise
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: requestModule
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: bigQuery
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: dataset
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: createReadStream
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: getRows_
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: setMaxListeners
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: getMaxListeners
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: emit
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: addListener
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: on
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: prependListener
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: once
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: prependOnceListener
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: removeListener
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: removeAllListeners
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: listeners
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: listenerCount
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table: eventNames
I      sendgridLoad     357398789515366  2019-01-15 04:02:32.247  table has import: false
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.446  job: kind
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  job: etag
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  job: id
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  job: selfLink
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  job: jobReference
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  job: configuration
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  job: status
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  job: statistics
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  job: user_email
I      sendgridLoad     357398789515366  2019-01-15 04:02:34.447  Job complete for 1547524950091000-396529d3-8fad-4bcb-b527-f1c24a3bb25b.json
D      sendgridLoad     357398789515366  2019-01-15 04:02:34.455  Function execution took 3395 ms, finished with status: 'ok'

よくわかってないですけど、実行が正常終了したっぽいです。jobの正体がイマイチ謎ですが、

Jobs  |  BigQuery  |  Google Cloud

ということでいいのかな?

sendgridLoadが動作するとBigQueryにデータが格納されていきます。

f:id:takeg:20190115144940p:plain

ということで、とりあえずチュートリアルは終了です。

cannot open source file "wchar.h" (dependency of "iostream") エラーを解消する

VSCodeC++環境を構築していました。

2018年版 C言語/C++ 入門者のための環境構築 (Windows編) - LYNCSブログ

C++で競プロをやるためのVSCodeの環境づくり - Qiita

上記のサイトを参考にしつつ構築していたら、VSCodeC++環境でcannot open source file "wchar.h" (dependency of "iostream")というエラーメッセージが出て困りました。ただ#include <iostream>しているだけなのに。

ちなみにWindows7 32bitです。VSCodeのextentionsはC/C++ (Preview)版。

そのときのc_cpp_properties.jsonの中身↓

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "${workspaceFolder}",
                "C:/MinGW/include",
                "C:/MinGW/lib/gcc/mingw32/6.3.0/include/c++",
                "C:/MinGW/lib/gcc/mingw32/6.3.0/include/c++/mingw32"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE"
            ],
            "compileCommands": "",
            "windowsSdkVersion": "8.1",
            "cStandard": "c11",
            "cppStandard": "c++14",
            "intelliSenseMode": "msvc-x64",
            "compilerPath": "C:/MinGW/bin/g++.exe"
        }
    ],
    "version": 4
}

色々調べた結果、compilerPathでg++.exeを指定しておきながらincludePathはgccを設定しているのでは?ということを疑う。compilerPathをgccに設定し直すことに。

と思ったのですが、C:/MinGW/bin/gcc.exeが入っていない。なんで。

C++コンパイラhttp://www.mingw.org/からダウンロードしたのですが、なにか間違ったのだろうか。

よくわからなかったのでTDM-GCCC++コンパイラをインストールします。するとC:\MinGW\TDM-GCC-32.5.1\bingcc.exeがインストールできました。

c_cpp_properties.jsongcc環境に書き換えます。

{
    "configurations": [
        {
            "name": "Win32",
            "includePath": [
                "${workspaceFolder}",
                "C:\\MinGW\\TDM-GCC-32.5.1\\include"
            ],
            "defines": [
                "_DEBUG",
                "UNICODE",
                "_UNICODE"
            ],
            "compileCommands": "",
            "windowsSdkVersion": "8.1",
            "cStandard": "c11",
            "cppStandard": "c++14",
            "intelliSenseMode": "msvc-x64",
            "compilerPath": "C:\\MinGW\\TDM-GCC-32.5.1\\bin\\gcc.exe"
        }
    ],
    "version": 4
}

するとエラーが消えました。

どうやらコンパイラがちゃんとインストールできていなかったか、g++とgccの設定を混同していたのが原因っぽいです。よくわからないですけど。

JDLAのG検定(ジェネラリスト検定)受かった

2018年11月24日開催のJDLA Deep Learning for GENERAL 2018#2に合格しました。

合格者の内訳は以下の通りです。

総受験者数 2,680名

合格者数  1,740名

約65%の合格率です。

合格証のPDFは後日配布だそうです。

勉強の際は以下のサイトを参考にしました。

JDLAのG検定に合格できたのでまとめてみる - エムティーアイ エンジニアブログ

どういう勉強したか

深層学習教科書 ディープラーニング G検定(ジェネラリスト) 公式テキストを2回読みました

上記の公式テキストですが、普通に読み物として良いですし、どのように人工知能研究が進んできたのかという歴史が知れるのでおすすめです。3回くらい読んで頭に叩き込んでれば半分くらいの問題は瞬殺できるのではないでしょうか。私は2回しか読んでないのでうろ覚えのところがありました。次やるなら3回読みますという自戒を込めて書いてます。

人工知能は人間を超えるか (角川EPUB選書)を1回読みました

人工知能は人間を超えるか」は公式の言う参考図書であり、読み物としても良いです。Kindleで1000円しないので買うと良いです。

適当に用語を検索して覚えた

上記の本だけでは本番の出題範囲は網羅できず、適当に用語を調べる必要があります。具体的には上で上げた参考サイト様が書いてあるように、例えばアンサンブル学習のブースティングやバギングって何?というようなものです。あとは簡単なF値の計算や偏微分の計算やプーリング層の計算とかありました。本当に簡単な計算なので、数学苦手な人でも基本的なことを知ってれば解けるはずです。プーリング層の畳み込み計算はパディング、ストライドを知ってる必要があります。本気で数学苦手なら大した問題数出ないので捨てても無問題ですが、デップラの試験受けといて数学から逃げていいのかという本質的な問題は残ります。

毎日はてブの「テクノロジー」の人気エントリーを適当に読んだ

これは別に勉強でやってるわけではなくて日課みたいなものなのですが、振り返ってみれば時事問題を解くのに役立ってました。時事問題は本に情報として残りにくく体系的に書きにくい情報なので、日々ニュースをウォッチするのがベターです(といっても時事問題や訴訟問題は公式テキストにある程度まとめられてあるので、そういう意味でも公式テキストは良かったです)。

その他

公式の参考図書は他にもあり、

などがあります。

これら読んでないのに受かっていいのだろうかという問題はあります。ただ大学時代に人工知能の研究をしてたのと、「ゼロから作るDeep Learning」という本を代わりにやっててその辺の知識も活きていたと思うので、「読まなくても受かった」というわけではなかったと思います。

本番当日のアドバイス

  • 紙とペンを用意する
  • 試験中調べることが可能なので、アプリのKindleで公式テキスト起動しておくと便利
  • 問題数に対して試験時間短いので、「調べすぎて時間足りなかった」に注意
  • 本番は「問題にチェック」という便利な機能があるので、それを使えば後から自信のない問題を見直すことが簡単にできる
  • ログインのテストはしておく
  • 試験開始直前に「あなたの試験一覧」に受験できる試験が表示されるので、当日焦らない(焦った)
  • TruePositive, FalseNegativeのマトリックス表は手元に書いておくと便利(これ頭の中でできないんだけど)
  • トイレにはいっておく

【C++】charからstringへの変換

C++全然わからん。

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main(int argc, char const *argv[]){
    string s = "ABC";
    map<string, int> mp;
    mp["A"] = 0;
    //mp[s[0]]++; // エラー
    mp[{s[0]}]++;
    cout << mp["A"] << endl;
    return 0;
}

char型のものは{}で囲めばstring型になってくれるらしい。

int main(int argc, char const *argv[]){
    char c = 'c';
    string s;
    s = {c};
    s = string({c});
    s = (string){c};
    
    //cout << {c} << endl;  // コンパイルエラー
    cout << string({c}) << endl;
    cout << (string){c} << endl;
    
    return 0;
}

ただし、上記のようにエラーになる場合があるので、string()で囲むのが無難か。

参考URL: http://marycore.jp/prog/cpp/convert-char-to-string/

PS: C++ってリファレンス読むのしんどい。

C++で文字列のスライス

C++全然わからない。

最近C++を使っているのですが、Pythonでいう文字列のスライスが使いたい。

s = "abcdef"
print(s[0:3])  # ==> "abc"

こんなの。

調べてもないっぽいのでそれっぽいものを実装。

#include <iostream>
#include <string>

using namespace std;

string str_slice(string s, int start, int end){
    if(end > s.size()){
        end = s.size();
    }
    if(end-start <= 0){
        return "";
    }
    return s.substr(start, end-start);
}

int main(void){
    string s = "abcdef";
    
    cout << str_slice(s, 0, 3) << endl;  // ==> "abc"
    cout << str_slice(s, 0, s.size()) << endl;  // ==> "abcdef"
    cout << str_slice(s, 0, 100) << endl;  // ==> "abcdef"
    cout << str_slice(s, 1, 0) << endl;  // ==> ""
    return 0;
}

start, endはint型でよいのだろうか。size_t型をよくわかっていない。 なるべくPythonっぽい挙動に似せた。Pythonはマイナス値取るけどあんまり使わないので実装していない。