20201025のC#に関する記事は14件です。

【C#】int型を1バイト区切りの16進/2進数文字列へ変換する拡張メソッド

0. はじめに

.NET標準のConvert.ToString( int value, int toBase )
10進数から2, 8, 16進数に変換した文字列を返してくれますが、
個人的に文字列の形式がイマイチな気がするのでピカイチにする拡張メソッドを作りました。
何らかの参考になればと:hotsprings:

1. こーしたい

例えば下のようにConvert.ToString()を使って数値を16進数、2進数に変換すると、

sample.cs
int n = 9999999;
Convert.ToString( n, 16 );
Convert.ToString( n, 2 );

出力は以下になります。

98967f
100110001001011001111111

この出力形式のイヤなところ:poop:
・バイトの区切り位置が分からん
・オール0のバイトは文字列に変換してくれない
・パッと見何バイトか分からん
・16進数の場合は小文字なのがなんかイヤ

こうあってほちかった:cry:

00 98 96 7F
00000000 10011000 10010110 01111111

2. こーしました

ということで、DIY(てめーでやれ)の精神にのっとり以下の拡張メソッドを作りますた。

Int32Extensions.cs
using System;
using System.Linq;

namespace MyLibrary.Extensions
{
    /// <summary>
    /// Int32 拡張メソッドクラス
    /// </summary>
    public static class Int32Extensions
    {
        /// <summary>
        /// 2進数表記の文字列に変換します。
        /// </summary>
        /// <param name="value">変換対象の値</param>
        /// <returns>16進数表記の文字列(形式:11111111 ... 11111111)</returns>
        public static string ToBin( this int value ) => value.ToAnyBase( 2, "11111111".Length );

        /// <summary>
        /// 16進数表記の文字列に変換します。
        /// </summary>
        /// <param name="value">変換対象の値</param>
        /// <returns>16進数表記の文字列(形式:FF FF FF FF)</returns>
        public static string ToHex( this int value ) => value.ToAnyBase( 16, "FF".Length );

        /// <summary>
        /// 8進数表記の文字列に変換します。
        /// </summary>
        /// <param name="value"></param>
        /// <returns>8進数表記の文字列(形式:037 777 777 777)</returns>
        public static string ToOct( this int value ) => value.ToAnyBase( 8, "777".Length );

        /// <summary>
        /// 10進数の値を、4桁区切りの基数表記の文字列に変換します。
        /// </summary>
        /// <param name="value">変換対象の値</param>
        /// <param name="toBase">変換対象の基数</param>
        /// <param name="length">変換対象の基数における、1バイトを表現可能な桁数(例: 16進数 = "FF" = 2 )</param>
        /// <returns></returns>
        private static string ToAnyBase( this int value, int toBase, int length )
        {
            var strs = Convert.ToString( value, toBase ).ToUpper()
                              .PadLeft( length * sizeof( int ), '0' )
                              .Select( ch => ch.ToString() );

            return string.Join( string.Empty, strs.Select( ( s, i ) =>
                                  ( i + 1 ) % length == 0 ? s + " " : s ) ).TrimEnd();
        }
    }
}

中核処理ToAnyBase()ToHex()ToBin()から呼び出して変換を行っています。
ちょこっと変更すればshortlongにも対応できそうです。
以下のように呼び出して使います。

sample.cs
int n = 9999999;
n.ToHex();
n.ToBin();

すると、冒頭にお見せした個人的ピカイチな文字列を返してくれます!やったぜ。:sunglasses:

00 98 96 7F
00000000 10011000 10010110 01111111

3. 他の n 進数にも変換したい

実はToAnyBase()内で使用しているConvert.ToString()の仕様で
2、8、10、16進数にしか対応できません。。。。
なので(実用的かどうかは別として)、5進数や7進数に変換したい場合は
別途処理をモニモニ書く必要があります。ゴメンナサイ (´ω`/ )

Convert.ToString()を笑うものはConv(ryに泣く

4.おわりに

今回はちょっとした小ネタを投下させていただきました。
現在アイディア募集中なので、
他にもこんな拡張メソッドが欲しい!いや作れ!などなど
コメントお待ちしておりまーす:postbox:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

タイマー作成②

初めに

投稿をサボってはいけない(戒め)

キー入力

「Mキーが押されたら~、Xキーが押されたら~」と仕様書にある通り、今回のタイマー作成に必須なのがこのキー入力。
フォームアプリケーションならKeyDownメソッドがあるから楽なのですが、今回は自分で作らないといけません。

ReadKey()

調べてみると、このConsoleクラスReadKeyメソッドでキー入力に関する処理を行えそうです。以下、公式サイトから。

オーバーロード
ReadKey()
ユーザーによって押された次の文字キーまたはファンクション キーを取得します。 押されたキーは、コンソール ウィンドウに表示されます。

ReadKey(Boolean)
ユーザーによって押された次の文字キーまたはファンクション キーを取得します。 押されたキーは、オプションでコンソール ウィンドウに表示されます。

・・・要するに
「ReadKeyメソッドは引数なしだと入力したキーを標準出力して、引数ありだと出力するかしないか選べるよ~」
ってことですね。今回はコンソール上に出力する必要はないのでConsole.ReadKey(false)を使うことになりそうですね。
逆でした。Console.ReadKey(true)で標準出力なしだそうです。普通逆だろぉ。

戻り値
押されたコンソール キーに対応する ConsoleKey 定数と Unicode 文字 (存在する場合) を記述するオブジェクト。 また、ConsoleKeyInfo オブジェクトは、1 つ以上の Shift、Alt、Ctrl の各修飾子キーがコンソール キーと同時に押されたかどうかを ConsoleModifiers 値のビットごとの組み合わせで記述します。

何を言ってるのかさっぱりですね。公式は日本語を喋ってほしい。

ConsoleKeyInfo

調べてみると、キー情報が格納されたConsoleKeyInfo構造体が戻り値として渡されるそうです。
・・・そろそろややこしくなってきました。構造体とクラスの違いについては、また後日。

プロパティ
Key
現在の ConsoleKeyInfo オブジェクトが表すコンソール キーを取得します。

KeyChar
現在の ConsoleKeyInfo オブジェクトが表す Unicode 文字を取得します。

Modifiers
コンソール キーと同時に押される 1 つ以上の修飾子キーを指定する、ConsoleModifiers 値のビットごとの組み合わせを取得します。

やっとそれらしきものにたどり着きました。
このKeyCharプロパティを使えば、キー入力時の処理を実現できそうですね!

デモ

今回のキー入力はタイマーというより、むしろユーザー側の操作に関係するので、新たにUserクラスを作成。

Userクラス

using System;

namespace Timer
{
    class User
    {
        public char PressedKey()
        {
            ConsoleKeyInfo keyInfo;
            if (Console.KeyAvailable)
            {
                keyInfo = Console.ReadKey(true);
                return keyInfo.KeyChar;
            }
            else
                return '\0';
        }
    }
}

Timerクラス

using System;

namespace Timer
{
    /// <summary>
    /// タイマー
    /// </summary>
    class Timer
    {
        //private int min = 0;
        //private int sec = 0;
        private User user;

        public Timer()
        {
            user = new User();
        }

        private int Min { get; set; }

        private int Sec { get; set; }
        /// <summary>
        /// 時間設定中の様子を表示
        /// </summary>
        public void printTimerSetting()
        {
            Console.CursorLeft = 0;
            Console.Write("時間を設定してください");
            Console.Write("Sキー:+10秒");
            Console.Write("Mキー:+1分");
            Console.Write("Cキー:リセット");
            Console.Write("Xキー:スタート/ストップ");            
            Console.Write("{0}分:{1}秒", Min, Sec);
        }

        /// <summary>
        /// タイマーの設定を行う
        /// </summary>
        public void settingTime()
        {
            char pressedKey;
            do
            {
                pressedKey = user.PressedKey();
                switch(pressedKey)
                {
                    case 's':
                        Sec += 10;
                        break;

                    case 'm':
                        Min++;
                        break;

                    case 'c':
                        Sec = 0;
                        Min = 0;
                        break;

                    case 'x':
                        Console.WriteLine("Xキーが押されました");
                        break;
                }
                printTimerSetting();
            } while (pressedKey != 'x');
        }
    }
}

Main

namespace Timer
{
    class main
    {
        public static void Main()
        {
            Timer timer = new Timer();

            timer.settingTime();
        }
    }
}

実行画面

実行画面.PNG

画像なので分かりづらいですが、Sキーを押すと10秒、Mキーを押すと1分追加され、Cキーを押すと値がリセット、そしてXキーを押すと「Xキーが押されました」と表示されて終了します。これでタイマーをセットするのは、概ね完成しましたね。

ただ、コンソールに上書き出力する時に文字がちらつくのが気になります・・・。どうにか改善しないと。

長くなりましたが、今回はここまで。
読んでいただき、ありがとうございます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# .NetCoreで、ボートレース結果の解析 構造検討

やること

C# .NetCoreで、ボートレース結果の解析の前調査
https://qiita.com/TamanegiTarao/items/02a634c36131ca85837e
をもとに、取得データ構造の検討を行う。
前回の記事では読みこみ迄やりたかったけど、長くなりそうだから
今回はここまでにする。

データ構造

前回の内容より、取得データの内容の検討する。
データ量が多くなることが想定されるため、
いくつかデータを以下の通り分ける。

節間タイトル

  • 名前
  • グレード情報
  • 開催期間

節間のタイトルに関する情報、グレード情報が解析時必要となるため。
グレード情報は手作業にならざるを得ない。

    public enum Internode1
    {
        General = 0,
        G3,
        G2,
        G1,
        SG,
    }

    public enum Internode2
    {
        Masters = 0,
        Wemen,
    }

    public class InternodeTitle
    {
        public string Title;
        public Internode1 Internode1;
        public Internode2 Internode2;
        public DateTime Start;
        public DateTime End;
    }

レース名

  • 名前
  • 特徴

レースの名前と特徴をまとめる。
特徴とは、進入固定や準優、優勝戦、シード番組など。
特徴をまとめるには手作業現状、手作業にならざるを得ない。

    public class RaceTitle
    {
        public string Title;
        public bool FixedApproach;
        public bool SemiFinal;
        public bool Final;
        public bool Seed;
    }

レース

  • 開催場
  • 開催日
  • 節間日
  • レース番号

レース内容の基本的な情報。レース結果と、レース情報の紐づけに使用する。

    public class Race
    {
        public int Place;
        public DateTime Date;
        public int InDay;
        public int RaceNum;
    }

レース結果

  • 単勝
  • 複勝
  • 2連単
  • 2連複
  • 3連単
  • 3連複
  • 各連複

配当情報。レース、選手に紐づけるのが目的。
組み合わせ、人気、配当が対象。

    public class ResultOne
    {
        public int Popler;
        public int Yen;
        public int First;
    }

    public class ResultTwo : ResultOne
    {
        public int Second;
    }

    public class ResultThree: ResultTwo
    {
        public int Thired;
    }


    public class RaceResult
    {
        public Race Race;
        public ResultOne Tansho;
        public ResultOne Fukusho1;
        public ResultOne Fukusho2;
        public ResultTwo NirenTan;
        public ResultTwo NirenFuku;
        public ResultThree SanrenTan;
        public ResultThree SanrenFuku;
        public ResultTwo Kakuren1;
        public ResultTwo Kakuren2;
        public ResultTwo Kakuren3;
    }

レース情報

  • 節間タイトル
  • レース名
  • レース結果
  • レース
  • 決まり手
  • 風向
  • 風速 レースのセンス情報以外の内容。 上記の、節間タイトル、レース名、レース結果を含む。
    public enum WindDirection { None, N, NE, E, ES, S, SW, W, WN };
    public enum Kimarite { Nige, Makuri, Sashi, Makurisashi, Megumare, Nuki, Huseiritsu }
    public class RaceInfo
    {
        public InternodeTitle InternodeTitle;
        public RaceTitle RaceTitle;
        public Race Race;
        public RaceResult RaceResult;
        public Kimarite Kimarite;
        public WindDirection WindDirection;
        public int WindMetor;
    }

選手成績

  • レース情報
  • 着順
  • 枠番
  • 進入
  • 登録番号
  • モーター
  • ボート
  • 展示
  • スタート

選手成績は選手に限定する情報のみにして、他の内容は、レース情報に紐づけておく。
レースタイムはあまり意味がない気がしているので除外する。

    public class PlayerResult
    {
        public RaceInfo RaceInfo;
        public int Number;
        public int Result;
        public int Wakuban;
        public int Cource;
        public int Motor;
        public int Boat;
        public double Tenji;
        public double Start;
    }

レース結果まとめ

  • 節間タイトルリスト
  • レース名リスト
  • レース結果リスト
  • 選手結果リスト

選手結果リストは、必要に応じてメンバリストの内容を紐づけることが前提。

    public class RaceResults
    {
        public List<InternodeTitle> InternodeTitles;
        public List<RaceTitle> RaceTitles;
        public List<RaceResult> Results;
        public List<PlayerResult> PlayerResults;
    }   

まとめ

今回はレースのデータ構造について検討を行った。
構造の是非はともかくこれで、要素ごとに分割した内容ができそうな予感はしている。
前置きが長くなってきているのが反省点。
次回は、実際にファイルを読んでみたいと思う。
だがしかし、すべてを読み込もうとするとボリュームが増えそうなので、
絞って記載しようかと考えている。

次回
C# .NetCoreで、ボートレース結果の解析 読み込みその1
https://qiita.com/TamanegiTarao/items/33284c3111a547b0ee8d

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# .NetCoreで、ボートレース結果の解析の前調査

やらなきゃいけないこと

  • 競争成績の内容を確認し、抽出方法を考えておく。

競争成績の取得

  • ボートレース公式→データを調べる→競争成績→日付を選択すればダウンロードすることができる。一時期気合でDLしていたファイルが一通り存在しているため、今回はスクレイピングなどは行わない。時期にやる。

フォーマット

  • ファイル名:Kyymmdd.TXT(yy:実施年、mm:実施月、dd:実施日)
    例:K191001.TXT(2019年10月1日の競争成績一覧)

- 基本的な流れ(K191001.TXTを例にとってみる)

STARTK
24KBGN
大 村[成績]     10/ 1      第6回夜の本命・波乱  第 5日

                            *** 競走成績 ***

          第6回夜の本命・波乱決定戦            

   第 5日          2019/10/ 1                             ボートレース大 村

               -内容については主催者発行のものと照合して下さい-

   [払戻金]       3連単           3連複           2連単         2連複
           1R  3-1-2    3360    1-2-3     260    3-1    1560    1-3     290
          ...略...
          12R  1-4-6    2010    1-4-6     810    1-4     530    1-4     390



   1R       一 般                    H1800m  曇り  風  北西  1m  波   1cm
  着 艇 登番  選 手 名  モーター ボート 展示 進入 スタートタイミンク レースタイム 差し   
-------------------------------------------------------------------------------
  01  3 2841 吉 田    稔 70   75  6.82   3    0.14     1.50.6
  ...略...
  06  5 3856 水 野  暁 広 43   21  6.79   5    0.24      .  . 

        単勝     3         2150  
        複勝     3          170  1          120  
        2連単   3-1       1560  人気     6 
        2連複   1-3        290  人気     2 
        拡連複   1-3        170  人気     3 
                 2-3        140  人気     2 
                 1-2        120  人気     1 
        3連単   3-1-2     3360  人気    12 
        3連複   1-2-3      260  人気     1 


  2~12Rは省略
24KEND
22KBGN
...略
05KEND
FINALK

大まかな流れ

  • レース結果以外の内容の取得
    • 開催場の取得
    • タイトルの取得
    • 開催日の取得
  • 払戻金の取得
  • レース結果の取得
    • レース結果(決まり手、気候、レース名など)
    • 各選手毎情報(着 登録番号 モーターなど)

情報量が増えるため、なるべく文字列でとらない方向で検討していく。

レース結果以外の内容の取得

取得範囲はこの部分になる。

STARTK
24KBGN
大 村[成績]     10/ 1      第6回夜の本命・波乱  第 5日

                            *** 競走成績 ***

          第6回夜の本命・波乱決定戦            

   第 5日          2019/10/ 1                             ボートレース大 村

24KBGNの[24]はボートレース大村の開催場コードとなっており、ここでどこの開催場かわかる。
次の行の10/ 1 第6回夜の本命・波乱 第 5日で開催日と開催日目がわかる。
その次の行の第6回夜の本命・波乱決定戦で節間タイトルがわかる。

見た感じ、String.split()するよりも、以下の関数を作成して、取り出すほうが現実的だと思う。

        string StringExtraction(string src, int st, int count)
        {
            if (encSJIS == null)
                encSJIS = Encoding.GetEncoding("Shift_JIS");
            byte[] rtnBytes = new byte[count];
            var srcbytes = encSJIS.GetBytes(src);
            Array.Copy(srcbytes, st, rtnBytes, 0, count);
            return encSJIS.GetString(rtnBytes);
        }

第一引数から、行文字列、開始位置、文字数で取得する。
やっている作業はShift-JISのバイト型配列に変換→開始位置→から文字数分抜き出し、stringに戻す作業を行っている。幸い元のファイルの文字コードがShift-JISであるため、漢字は2文字カウントとすることで、扱いやすくできる。実際の文字数ではないため、テキストエディタで、文字数を確認しながら位置を確認していけばいい。

払戻金の取得

取得範囲はこの部分になる。

   [払戻金]       3連単           3連複           2連単         2連複
           1R  3-1-2    3360    1-2-3     260    3-1    1560    1-3     290
          ...略...
          12R  1-4-6    2010    1-4-6     810    1-4     530    1-4     390

左から、レース番号、3連単[組、配当]、3連複[組、配当]、2連単[組、配当]、2連複[組、配当]の順となっている。構成もシンプルであるため、String.Split(' ', StringSplitOptions.RemoveEmptyEntries)で簡単に分割が行えそうである。

しかし気づいてしまった。ここの情報には人気が記載されていない。
そのため、

        単勝     3         2150  
        複勝     3          170  1          120  
        2連単   3-1       1560  人気     6 
        2連複   1-3        290  人気     2 
        拡連複   1-3        170  人気     3 
                 2-3        140  人気     2 
                 1-2        120  人気     1 
        3連単   3-1-2     3360  人気    12 
        3連複   1-2-3      260  人気     1 

取得したほうがいい気がする。
いずれのレースも上記順番であるため、String.Split(' ', StringSplitOptions.RemoveEmptyEntries)で簡単に分割が行えそうである。
直前行の内容から、問題なくレース番号の紐づけも取得できそう。

レース結果

   1R       一 般                    H1800m  曇り  風  北西  1m  波   1cm
  着 艇 登番  選 手 名  モーター ボート 展示 進入 スタートタイミンク レースタイム 差し   
-------------------------------------------------------------------------------
  01  3 2841 吉 田    稔 70   75  6.82   3    0.14     1.50.6
  ...略...
  06  5 3856 水 野  暁 広 43   21  6.79   5    0.24      .  . 
1R       一 般                    H1800m  曇り  風  北西  1m  波   1cm

この行で、該当レース番号と、結果、天気、風向、風力、の取得が行える。
構成もシンプルであるため、
String.Split(' ', StringSplitOptions.RemoveEmptyEntries)
簡単に分割が行えそう。

着 艇 登番  選 手 名  モーター ボート 展示 進入 スタートタイミンク レースタイム 差し 

この行には、決まり手がスペース分割後の最終項に記載されているため、取得が必要である。
本行もString.Split(' ', StringSplitOptions.RemoveEmptyEntries)
簡単に分割が行えそう。

01  3 2841 吉 田    稔 70   75  6.82   3    0.14     1.50.6

この行が、各選手の情報になる。
着順、艇番、登録番号、選手名、モーター、ボート、展示、進入、スタートタイミング、レースタイム
の順番に記載されている。
本例の場合は、シンプルな構成に見えるが、選手がFを切った場合や失格、欠場になった場合など、複雑なパターンが容易に想定されるので、
String.Split(' ', StringSplitOptions.RemoveEmptyEntries)関数を使用しての取り出しのほうがよさそう。レースタイムも、変な感じだし。おそらく欠場の場合も似たようになるでしょう。

まとめ

本項では、データの表示内容のフォーマットを確認でき、文字列からデータの取り出し方に
関して検討を進めることができた。
次回は、実際のデータの取得と、取得データの分け方の検討について書きたいと思う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# .NetCoreで、ボートレース結果の解析 前調査

やらなきゃいけないこと

  • 競争成績の内容を確認し、抽出方法を考えておく。

競争成績の取得

  • ボートレース公式→データを調べる→競争成績→日付を選択すればダウンロードすることができる。一時期気合でDLしていたファイルが一通り存在しているため、今回はスクレイピングなどは行わない。時期にやる。

フォーマット

  • ファイル名:Kyymmdd.TXT(yy:実施年、mm:実施月、dd:実施日)
    例:K191001.TXT(2019年10月1日の競争成績一覧)

- 基本的な流れ(K191001.TXTを例にとってみる)

STARTK
24KBGN
大 村[成績]     10/ 1      第6回夜の本命・波乱  第 5日

                            *** 競走成績 ***

          第6回夜の本命・波乱決定戦            

   第 5日          2019/10/ 1                             ボートレース大 村

               -内容については主催者発行のものと照合して下さい-

   [払戻金]       3連単           3連複           2連単         2連複
           1R  3-1-2    3360    1-2-3     260    3-1    1560    1-3     290
          ...略...
          12R  1-4-6    2010    1-4-6     810    1-4     530    1-4     390



   1R       一 般                    H1800m  曇り  風  北西  1m  波   1cm
  着 艇 登番  選 手 名  モーター ボート 展示 進入 スタートタイミンク レースタイム 差し   
-------------------------------------------------------------------------------
  01  3 2841 吉 田    稔 70   75  6.82   3    0.14     1.50.6
  ...略...
  06  5 3856 水 野  暁 広 43   21  6.79   5    0.24      .  . 

        単勝     3         2150  
        複勝     3          170  1          120  
        2連単   3-1       1560  人気     6 
        2連複   1-3        290  人気     2 
        拡連複   1-3        170  人気     3 
                 2-3        140  人気     2 
                 1-2        120  人気     1 
        3連単   3-1-2     3360  人気    12 
        3連複   1-2-3      260  人気     1 


  2~12Rは省略
24KEND
22KBGN
...略
05KEND
FINALK

大まかな流れ

  • レース結果以外の内容の取得
    • 開催場の取得
    • タイトルの取得
    • 開催日の取得
  • 払戻金の取得
  • レース結果の取得
    • レース結果(決まり手、気候、レース名など)
    • 各選手毎情報(着 登録番号 モーターなど)

情報量が増えるため、なるべく文字列でとらない方向で検討していく。

レース結果以外の内容の取得

取得範囲はこの部分になる。

STARTK
24KBGN
大 村[成績]     10/ 1      第6回夜の本命・波乱  第 5日

                            *** 競走成績 ***

          第6回夜の本命・波乱決定戦            

   第 5日          2019/10/ 1                             ボートレース大 村

24KBGNの[24]はボートレース大村の開催場コードとなっており、ここでどこの開催場かわかる。
次の行の10/ 1 第6回夜の本命・波乱 第 5日で開催日と開催日目がわかる。
その次の行の第6回夜の本命・波乱決定戦で節間タイトルがわかる。

見た感じ、String.split()するよりも、以下の関数を作成して、取り出すほうが現実的だと思う。

        string StringExtraction(string src, int st, int count)
        {
            if (encSJIS == null)
                encSJIS = Encoding.GetEncoding("Shift_JIS");
            byte[] rtnBytes = new byte[count];
            var srcbytes = encSJIS.GetBytes(src);
            Array.Copy(srcbytes, st, rtnBytes, 0, count);
            return encSJIS.GetString(rtnBytes);
        }

第一引数から、行文字列、開始位置、文字数で取得する。
やっている作業はShift-JISのバイト型配列に変換→開始位置→から文字数分抜き出し、stringに戻す作業を行っている。幸い元のファイルの文字コードがShift-JISであるため、漢字は2文字カウントとすることで、扱いやすくできる。実際の文字数ではないため、テキストエディタで、文字数を確認しながら位置を確認していけばいい。

払戻金の取得

取得範囲はこの部分になる。

   [払戻金]       3連単           3連複           2連単         2連複
           1R  3-1-2    3360    1-2-3     260    3-1    1560    1-3     290
          ...略...
          12R  1-4-6    2010    1-4-6     810    1-4     530    1-4     390

左から、レース番号、3連単[組、配当]、3連複[組、配当]、2連単[組、配当]、2連複[組、配当]の順となっている。構成もシンプルであるため、String.Split(' ', StringSplitOptions.RemoveEmptyEntries)で簡単に分割が行えそうである。

しかし気づいてしまった。ここの情報には人気が記載されていない。
そのため、

        単勝     3         2150  
        複勝     3          170  1          120  
        2連単   3-1       1560  人気     6 
        2連複   1-3        290  人気     2 
        拡連複   1-3        170  人気     3 
                 2-3        140  人気     2 
                 1-2        120  人気     1 
        3連単   3-1-2     3360  人気    12 
        3連複   1-2-3      260  人気     1 

取得したほうがいい気がする。
いずれのレースも上記順番であるため、String.Split(' ', StringSplitOptions.RemoveEmptyEntries)で簡単に分割が行えそうである。
直前行の内容から、問題なくレース番号の紐づけも取得できそう。

レース結果

   1R       一 般                    H1800m  曇り  風  北西  1m  波   1cm
  着 艇 登番  選 手 名  モーター ボート 展示 進入 スタートタイミンク レースタイム 差し   
-------------------------------------------------------------------------------
  01  3 2841 吉 田    稔 70   75  6.82   3    0.14     1.50.6
  ...略...
  06  5 3856 水 野  暁 広 43   21  6.79   5    0.24      .  . 
1R       一 般                    H1800m  曇り  風  北西  1m  波   1cm

この行で、該当レース番号と、結果、天気、風向、風力、の取得が行える。
構成もシンプルであるため、
String.Split(' ', StringSplitOptions.RemoveEmptyEntries)
簡単に分割が行えそう。

着 艇 登番  選 手 名  モーター ボート 展示 進入 スタートタイミンク レースタイム 差し 

この行には、決まり手がスペース分割後の最終項に記載されているため、取得が必要である。
本行もString.Split(' ', StringSplitOptions.RemoveEmptyEntries)
簡単に分割が行えそう。

01  3 2841 吉 田    稔 70   75  6.82   3    0.14     1.50.6

この行が、各選手の情報になる。
着順、艇番、登録番号、選手名、モーター、ボート、展示、進入、スタートタイミング、レースタイム
の順番に記載されている。
本例の場合は、シンプルな構成に見えるが、選手がFを切った場合や失格、欠場になった場合など、複雑なパターンが容易に想定されるので、
String.Split(' ', StringSplitOptions.RemoveEmptyEntries)関数を使用しての取り出しのほうがよさそう。レースタイムも、変な感じだし。おそらく欠場の場合も似たようになるでしょう。

まとめ

本項では、データの表示内容のフォーマットを確認でき、文字列からデータの取り出し方に
関して検討を進めることができた。
次回は、実際のデータの取得と、取得データの分け方の検討について書きたいと思う。

次回
C# .NetCoreで、ボートレース結果の解析 構造検討
https://qiita.com/TamanegiTarao/items/04f43d96e50cb3eeaa03

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# .NetCoreで、ボートレース解析をしてみよう。

本ページは意気込みを語っています。

概要

ボートレース(競艇)は公式サイトにてレース結果や選手情報の
データ無料で取得できるようになっています。
そのデータをまとめて、いろんな角度からデータを解析することを
目的としています。

環境

  • Windows10
  • Visual Studio Community 2019
  • C# .Net Core 理由は特にないです。慣れてるからだけ。 pythonも考えたけど、まずは勝手掴んでいるC#でやってみる。

リンク

C# .NetCoreで、ボートレース結果の解析の前調査
https://qiita.com/TamanegiTarao/items/02a634c36131ca85837e

終わりに

ご意見、感想ございましたらお気軽に。
モチベーションにつながると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# .NetCoreで、ボートレース結果解析 してみよう

本ページは意気込みを語っています。

概要

ボートレース(競艇)は公式サイトにてレース結果や選手情報を
データ無料で取得できるようになっています。
そのデータをまとめて、いろんな角度からデータを解析を行えるように
することを目的としています。

環境

  • Windows10
  • Visual Studio Community 2019
  • C# .Net Core 理由は特にないです。慣れてるからだけ。 pythonも考えたけど、まずは勝手掴んでいるC#でやってみる。

リンク

C# .NetCoreで、ボートレース結果の解析 前調査
https://qiita.com/TamanegiTarao/items/02a634c36131ca85837e
C# .NetCoreで、ボートレース結果の解析 構造検討
https://qiita.com/TamanegiTarao/items/04f43d96e50cb3eeaa03
C# .NetCoreで、ボートレース結果の解析 読み込みその1
https://qiita.com/TamanegiTarao/items/33284c3111a547b0ee8d

終わりに

ご意見、感想ございましたらお気軽に。
モチベーションにつながると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity「Add Script Component "****"…」が表示されC#スクリプトがアタッチできない

Unitでオブジェクトに作りたてのC#Scriptをアタッチすると
Can't Add Script - Add Script Component "***"
と表示されてアタッチできない事がある

いくつか要因があるようで、まとめる。
※関係ないものもあるかもしれませんがご容赦を。
 私の場合はおそらく3,4,5,6,7が原因でした

1.ファイル名とクラス名が異なる場合
 ファイル名がTestの場合、クラス名もTestにする必要がある。(ファイルをリネームした場合クラス名に反映されない。逆もしかり)
 <参考資料>
 https://freesworder.net/unity-cant-add-script/

2.Unity Hubのバグ
 <参考資料>
 https://teratail.com/questions/229361

3.Visual Studio に「Unityによるゲーム開発」
 がインストールされていない
 <参考資料>
 http://www.code-magagine.com/?p=3854

4.Visual Studioが最新バージョンでない
 <参考資料>
 http://www.code-magagine.com/?p=3854

5.デバッガ(Visual Studio)がUnityにアタッチされていない
 下記手順で解決することがある
 ①スクリプトをダブルクリックし、Visual Studioを起動
 ②メニューバー→デバッガ→Unityデバッガのアタッチを選択
 ③ウィンドウが開くので該当するUnityのインスタンスを選択
 <参考資料>
 http://www.code-magagine.com/?p=3854

6.Standard Assetsをインポートしている場合
 Visual Studioでビルドすると「GUITextが旧形式です」と表示される場合はこれに該当する
 SimpleActivatorMenu.cs内に
 using UnityEngine.UI;を追加
 GUIText→Textに書き換える
 <参考資料>
 https://connect.unity.com/p/standard-assets-guitexture-and-guitext-are-obsolete

7.上記を実施した上で再起動
 最後はUnityとVIsual Studioを再起動したところでビルドが通り解決しました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めての.NET 5

rksoftware君がblogを書いているので、追っかけてみます

ここから初めて
https://devblogs.microsoft.com/dotnet/announcing-net-5-0-rc-1/
RC1 is a “go live” release とあるので、実開発にも使えるのね

Visual Studio 2019 の Preview 版をインストールが前提みたいですが
https://visualstudio.microsoft.com/ja/vs/preview/
VisualStudioのインストーラーにはないので
ここから
https://dotnet.microsoft.com/download/dotnet/5.0
これを
https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.100-rc.1-windows-x64-installer
ダウンロードします

.NET 5 SDK をインストール (私は Windows の x64 インストーラーでインストールしました)

じゃ、まずは、Webアプリのプロジェクトを作ってみます

新しいプロジェクトの作成から
image.png

ASP.NET Core Web アプリケーションを選択

image.png

Web アプリケーションを選択

image.png

SDK が入っているので プルダウンで ASP.NET Core 5.0 を選択可能です。

image.png

見慣れた、光景ですね。

起動すると

image.png

いつものProgram.cs

Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApplication10
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

いつもの、StartUp.cs

StartUp.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace WebApplication10
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
            });
        }
    }
}

3.1からの移行は、そんなに困らなさそう?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - ブランク領域を含むmotファイル(S-Record)を入力として複数のbinファイルを出力する - 自分用

入出力のイメージ

入力

sample.mot
S113000000000100000001000000010000000100E8
S113001000000100000001000000010000000100D8
S113002000000100000001000000010000000100C8
S11300D00000010000000100000001000000010018
S11300E00000010000000100000001000000010008
S10700F00000010007

出力(中身はただのバイナリ)

生成されるファイル
_00000000_0000002F.bin
_000000D0_000000F3.bin

入力motに対する前提条件

  • アドレスが昇順になっていない場合の連結は未サポート(つながった領域でも細切れにbinが生成されるはず。)
  • intの最大値の半分(1024MB相当)以上の大きさのデータは多分扱えないです。1

ソースコード

なぐり書きコードでろくにテストもしてないので、参考程度ということにしてください。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;


internal class DataBlocks
{
    // Key: address
    public SortedList<long, DataBlock> Blocks{get;private set;}

    DataBlock _lastAdded;
    public bool ConsistencyPassed{get;private set;}

    public DataBlocks()
    {
        Blocks = new SortedList<long, DataBlock>();
        _lastAdded = null;
        ConsistencyPassed = false;
    }

    // エラー(重複検出時)は return false
    public bool Append(DataBlock b)
    {
        if ( b.Size == 0 ) {
            return true; // 長さがないデータは無視する(正常扱い)
        }

        if ( _lastAdded == null ) { // まだデータがない
            Blocks.Add(b.Address, b);
            _lastAdded = b;
            return true;
        }

        ConsistencyPassed = false;

        if ( Blocks.ContainsKey(b.Address) ) {
            // overlap
            return false;
        }

        if ( _lastAdded.TryAppendBlock(b) ) {
            // 最終要素に連結できた。
            // nothing to do.
            return true;
        }
        else {
            Blocks.Add(b.Address, b);
            _lastAdded = b;
            return true;
        }
    }

    // overlapがないかチェック。
    // (memo: 連結できれば連結するようにしたい TryToConcatAndCheckNoOverlap ... これは未実装)
    public bool CheckNoOverlap()
    {
        DataBlock prevBlock = null;

        foreach ( var b in Blocks.Values ) {
            if ( prevBlock != null ){
                if ( b.IsOverlap(prevBlock) ) {
                    return false;
                }
                //else if ( prevBlock.EndAddress+1 == b.Address ) {  // 連結可能
                    //  ただ、foreach内でこれをやると不整合が起きそう
                    // 未実装
                //}
            }
            prevBlock = b;
        }
        ConsistencyPassed = true;
        return true;
    }


    public static DataBlocks FromMotFile(string fileName)
    {
        var res = new DataBlocks();

        string[] ss = File.ReadAllLines(fileName);
        int lineNo = 0;

        foreach ( string s in ss ) {
            lineNo++;
            if (String.IsNullOrWhiteSpace(s)) {
                continue;
            }

            var oneRecord = OneRecord.FromString(s);
            if ( oneRecord == null ) {
                Console.WriteLine("Warning: Line " + lineNo.ToString() + " is ignored.");
            }
            else {
                if ( !res.Append(DataBlock.FromOneRecord(oneRecord)) ) {
                    // conflicted(overlapp)
                    Console.WriteLine("Warning: Line " + lineNo.ToString() + " is ignored (start address conflicted).");
                }
            }
        }
        return res;
    }
} 

internal class DataBlock
{
    public long Address{get;private set;}
    public long EndAddress{get{return Address + Size - 1;}}
    public long Size{get;private set;} // 有効データ部分のサイズ
    long InternalSize{get{return Data.Length;}}

    // Array.Resize が ref を使っており、実体をフィールド宣言せざるをえない。
    // (プロパティはref/outで参照できない。)
    byte[] _data;
    public byte[] Data{get{return _data;}private set{_data=value;}} // バッファとして確保しているサイズを含む

    public DataBlock(long addr, byte[] data)
    {
        Address = addr;
        Size = data.Length;
        Data = new byte[data.Length];
        Array.Copy(data,0,Data,0,Size);
    }

    public static DataBlock FromOneRecord(OneRecord record)
    {
        return new DataBlock(record.Address, record.DataBytes);
    }

    // 有効データが入っている部分のデータを返す
    public byte[] GetActualDataBytes()
    {
        if ( Size == InternalSize ) {
            return Data;
        }
        else {
            byte[] t = new byte[Size];
            Array.Copy(Data, 0, t, 0, Size);
            return t;
        }
    }

    public void SaveAsBinaryFile(string destPath)
    {
        File.WriteAllBytes(destPath, GetActualDataBytes());
    }

    // アドレスが後ろ側につながっていれば連結する。
    // 結合できる(=できた)場合は true を返す。
    public bool TryAppendBlock(DataBlock b)
    {
        if (EndAddress+1 == b.Address) {
            if ( InternalSize < Size + b.Size ) { // サイズが足りない。
                int newInternalSize;
                checked {
                    // リサイズする
                    newInternalSize = (int)(Size + b.Size);
                    if ( newInternalSize < (int)(2*Size) ) {
                        // リサイズが必要となる頻度を抑制するため、大きめに確保しておく
                        newInternalSize = (int)(2*Size);
                    }
                }
                Array.Resize(ref _data, newInternalSize);
            }
            Array.Copy(b.Data, 0, Data, Size, b.Size); // bの有効データを、thisの有効データの末尾(位置=Size)以降にコピーする
            Size += b.Size;
            return true;
        }
        return false;
    }

    public bool IsOverlap(DataBlock b)
    {
        return !IsExclusive(b);
    }

    bool IsExclusive(DataBlock b)
    {
        if ( EndAddress < b.Address ){ return true; }
        if ( b.EndAddress < Address ){ return true; }
        return false;
    }
} 


// S-Record 1行分のデータを扱う
class OneRecord
{
    // This program ignores S5, S7, S8, S9.
    static Regex rSn = new Regex(@"^S([01235789])");
    static Regex rS0 = new Regex(@"^S(0)([0-9A-Fa-f]{2})((?:[0-9A-Fa-f]{2})*)([0-9A-Fa-f]{2})$");
    static Regex rS1 = new Regex(@"^S(1)([0-9A-Fa-f]{2})([0-9A-Fa-f]{4})((?:[0-9A-Fa-f]{2})+)([0-9A-Fa-f]{2})$");
    static Regex rS2 = new Regex(@"^S(2)([0-9A-Fa-f]{2})([0-9A-Fa-f]{6})((?:[0-9A-Fa-f]{2})+)([0-9A-Fa-f]{2})$");
    static Regex rS3 = new Regex(@"^S(3)([0-9A-Fa-f]{2})([0-9A-Fa-f]{8})((?:[0-9A-Fa-f]{2})+)([0-9A-Fa-f]{2})$");

    public int    RecordType{get;private set;}
    public int    RecordLength{get;private set;}
    public long   Address{get;private set;}
    public byte[] DataBytes{get;private set;}
    public byte   CheckSum{get;private set;}

    public int    DataLength{get{return DataBytes.Length;}}
    public long   EndAddress{get{return Address + DataBytes.Length -1;}}

    public bool IsValidSum{get{return CalcCheckSum()==CheckSum;}}

    byte CalcCheckSum()
    {
        int sum=0;
        sum +=  RecordLength;
        sum += (byte)(Address>>24);
        sum += (byte)(Address>>16);
        sum += (byte)(Address>> 8);
        sum += (byte) Address;
        foreach(byte b in DataBytes) {
            sum += b;
        }
        return (byte)(~sum);
    }


    OneRecord()
    {
    }

    public static OneRecord FromString(string s, bool checkFormat = true)
    {
        Match m;
        m = rSn.Match(s);
        if ( !m.Success ) {
            return null;
        }
        m = rS1.Match(s);
        if ( !m.Success ) { m = rS2.Match(s); }
        if ( !m.Success ) { m = rS3.Match(s); }
        if ( !m.Success ) { return null; }

        var ret = new OneRecord();

        ret.RecordType   = Convert.ToInt32(m.Groups[1].Value);
        ret.RecordLength = Convert.ToInt32(m.Groups[2].Value, 16);
        ret.Address      = Convert.ToInt64(m.Groups[3].Value, 16);
        ret.DataBytes    = HexStrToByteArray(m.Groups[4].Value);
        ret.CheckSum     = Convert.ToByte(m.Groups[5].Value, 16);

        if ( checkFormat ) {
            if ( ret.RecordLength*2 != s.Length - 4) {
                Console.WriteLine("LengthError");
                return null;
            }
            if ( !ret.IsValidSum ){
                Console.WriteLine("ChkSumError");
                return null;
            }
        }
        return ret;
    }

    static int HexCharToInt(char c)
    {
        if('0'<=c&&c<='9'){return  c-'0';}
        if('A'<=c&&c<='F'){return (c-'A')+10;}
        if('a'<=c&&c<='f'){return (c-'a')+10;}
        return -1;
    }

    static byte[] HexStrToByteArray(string s)
    {
        // length of the argument `s` must be even value. 
        byte[] a = new byte[s.Length/2];
        for (int pos=0 ; pos+1<s.Length ; pos+=2) {
            a[pos/2] = (byte)( (HexCharToInt(s[pos])<<4) | HexCharToInt(s[pos+1]) );
        }
        return a;
    }
}

class MotFileTest
{
    [STAThread]
    static void Main(string[] args)
    {
        if ( args.Length != 1 ) { return; }

        var dataBlocks = DataBlocks.FromMotFile(args[0]);
        if ( dataBlocks.CheckNoOverlap() ) {
            foreach( var block in dataBlocks.Blocks.Values ) {
                string fileName = Path.GetFullPath("_"+block.Address.ToString("X08")+"_"+block.EndAddress.ToString("X08")+".bin");
                Console.WriteLine(fileName);
                block.SaveAsBinaryFile(fileName);
            }
        }
    }
}

ひとりごと

アドレスでソートしておいたほうがデータを扱いやすいかなと思い、SortedListを初めて使ってみた。
ListよりDictionaryに近い感じ(?)

参考サイト


  1. おそらく下記コードのTryAppendBlockあたりの処理でオーバーフローするはず。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】値型と参照型・値渡しと参照渡し

はじめに

C#を勉強し始めて1年以上経過したので自分の備忘録としての投稿。
実際にコードを書いていくうちに値型と参照型、値渡しと参照渡しの違いをちゃんと知っておくことの大切さを知ったので記事にしました。
間違いなどありましたらコメント欄にてご指摘いただけると幸いです。

目次

 1.変数の種類(値型と参照型)
 2.値渡しと参照渡し
 3.まとめ

1.変数の種類(値型と参照型)

よくある参考書では「変数とは箱のようなものである」と説明されていますが、
実はその覚え方あまりよくないかもしれません。

変数には大きく分けて値型参照型の2種類があり、それぞれ値の持ち方が違います。
どのように違うかを実際のソースコードと図を交えながら解説していきます。

例えば以下のように変数に値を代入した場合を考えてみます。

  int x = 12;
  string y = "apple";

int型の変数xには12、string型の変数yには"apple"を代入しています。
「 変数 = 箱 」と考えている人の中には以下のようなイメージを持っている人が多いかもしれません。

変数の中身のイメージ ※実際は違う

qiita2_resize.png

どちらも変数(箱)に値を入れているのだからあってるじゃんと思うかもしれません。
しかし実際の変数の中身は以下のようになります。

実際の変数の中身のイメージ

qiita_resize.png

int型の場合は変数xという箱の中に12という値が入っているのでここはイメージ通りかと思います。
問題は「string型変数y」の方です。

int型の変数xとは違い、string型の変数yという箱の中には文字列の"apple"そのものではなく実際に"apple"が置かれている場所のアドレスが入っています。

「置かれている場所のアドレス・・・? なにそれ??」という声が聞こえてきそうですが
ひとまずは箱の中に入っているのは"apple"という文字列そのものではなくアドレスなんだなぁくらいの認識でOKです。
実はこれが冒頭に出てきた値型参照型値の持ち方の違いなんです。

メモリにはスタックヒープという2種類の領域があります。

値型

値型の変数はスタックに領域が確保され、確保された領域内に値が置かれます。
(上の例ではint型の変数xが使用する領域がスタック上に確保され、確保された領域に12が置かれます。)

参照型

参照型の変数はスタックに領域が確保されますが、値そのものはヒープ上に置かれます。
スタック上に確保された領域にはヒープ上にある値の場所を示すアドレス(ヒープ上にある値の場所を表すもの)が置かれます。
(上の例ではstring型の変数yが使用する領域がスタックに確保され、確保された領域にはヒープ上に置かれた"apple"の場所を表すアドレス値が置かれます。)

2.値渡しと参照渡し

変数には値型と参照型があるということを前項で説明しました。
この項ではメソッドへの引数の渡し方について説明していきます。

説明中に実引数と仮引数という言葉が出てきますので、うろ覚えの方はここで覚えておいてください。

実引数

メソッドを呼び出す際に実際に渡す値の事を実引数と言います。

  int x = 12;
  string y = "apple";

  SampleMethod(x, y); // xとyは実引数

仮引数

メソッドを呼び出す際に値を受け取る変数の事を仮引数と言います。

  void SampleMethod(int x, string y) // xとyは仮引数
  {
    // 引数を用いた何らかの処理
  }

値渡し

仮引数に対して、実引数に指定したの値のコピーを渡す事を値渡しと言います。
仮引数の値を変更しても実引数に指定した変数には影響がありません。

  static void Main()
  {
      // 呼び出し元
      SampleMethod(x, y)  
  }

  void SampleMethod(int x, string y) 
  {
    // 引数を用いた何らかの処理
  }

参照渡し

仮引数に対して、実引数に指定した値が格納されている場所(アドレス)を渡すことを参照渡しと言います。
仮引数に値を変更すると実引数に指定した変数にも変更の内容が反映されます。
(C#ではref/in/outを実引数・仮引数に追加すると参照渡しとなる)

  static void Main()
  {
      // 呼び出し元
      SampleMethod(ref x, ref y) 
  }

  void SampleMethod(ref int x, ref string y)  // 参照渡しでは仮引数の型指定の前にrefを追加する
  {
    // 引数を用いた何らかの処理
  }

上の説明だけではイマイチわかりづらいと思うので以下にサンプルコードを提示します。
値渡し・参照渡しのイメージを掴んでみてください。

    class Program
    {
        static void Main()
        {

            // クラスのインスタンスを生成(参照型)
            SampleClass samClass1 = new SampleClass();
            RefVal(samClass1);
            System.Console.WriteLine("samClass1.x = " + samClass1.x); // 結果:samClass1.x = 100

            SampleClass samClass2 = new SampleClass();
            RefRef(ref samClass2);
            System.Console.WriteLine("samClass2.x = " + samClass2.x); // 結果:samClass2.x = 100

            // 構造体のインスタンスを生成(値型)
            SampleStruct samStruct1 = new SampleStruct();
            ValVal(samStruct1);
            System.Console.WriteLine("samStruct1.x = " + samStruct1.x); // 結果:samStruct1.x = 0

            SampleStruct samStruct2 = new SampleStruct();
            ValRef(ref samStruct2);
            System.Console.WriteLine("samStruct2.x = " + samStruct2.x); // 結果:samStruct2.x = 100

        }

        // 参照型の値渡し
        static void RefVal(SampleClass sam)
        {
            sam.x = 100;
        }

        // 参照型の参照渡し
        static void RefRef(ref SampleClass sam)
        {
            sam.x = 100;
        }

        // 値型の値渡し
        static void ValVal(SampleStruct sam)
        {
            sam.x = 100;
        }

        // 値型の参照渡し
        static void ValRef(ref SampleStruct sam)
        {
            sam.x = 100;
        }
    }

    // クラス(参照型)
    class SampleClass
    {
        public int x;
    }

    // 構造体(値型)
    struct SampleStruct
    {
        public int x;
    }


上のソースについてそれぞれ解説していきます。

参照型の値渡し

まず参照型の値渡しですが、上でお話したことを思い出してみてください。
それぞれ以下のようになります。
参照型:変数にはアドレスを保持している
値渡し:変数の値のコピーを渡す

つまり、アドレスのコピーを渡しているのでsamClass1とsamはともに同じアドレスを格納しているということです。
アドレスが同じなのでsamが参照しているオブジェクトに対して行った変更はsamClass1にも反映されます。
(同じものを参照しているのですから、冷静に考えてみると当たり前ですね(笑))

qiita4.png

    // コード抜粋
    static void Main()
    {
        // クラスのインスタンスを生成(参照型)
        SampleClass samClass1 = new SampleClass();
        RefVal(samClass1);
        System.Console.WriteLine("samClass1.x = " + samClass1.x); // 結果:samClass1.x = 100
    }

    // 参照型の値渡し
    static void RefVal(SampleClass sam)
    {
        sam.x = 100; // samとsamClass1の変数の中身はともに同じアドレスが格納されている
    }

参照型の参照渡し

参照型の参照渡しについて
それぞれ以下のようになります。
参照型 :変数にはアドレスを保持している
参照渡し:変数の場所(アドレス)を渡す

つまり、オブジェクトの場所(アドレス)を保持している変数の場所(アドレス)を渡しているということです。
以下の画像のような経路でオブジェクトを参照することでxの値を変更しています。

qiita3.png

    // コード抜粋
    static void Main()
    {
        // クラスのインスタンスを生成(参照型)
        SampleClass samClass2 = new SampleClass();
        RefRef(ref samClass2);
        System.Console.WriteLine("samClass2.x = " + samClass2.x); // 結果:samClass2.x = 100
    }

    // 参照型の参照渡し
    static void RefRef(ref SampleClass sam)
    {
        sam.x = 100;
    }

値型の値渡し

値型の値渡しについて
それぞれ以下のようになります。
値型 :変数にはを保持している
値渡し:変数の値のコピーを渡す

つまり、samとsamStructは別物なので、samのxを100に変更してもsamStruct1のxは変更されません。
qiita5.png

    // コード抜粋
    static void Main()
    {
        // 構造体のインスタンスを生成(値型)
        SampleStruct samStruct1 = new SampleStruct();
        ValVal(samStruct1);
        System.Console.WriteLine("samStruct1.x = " + samStruct1.x); // samStruct1.x = 0
    }

    // 値型の値渡し
    static void ValVal(SampleStruct sam)
    {
        sam.x = 100;
    }

値型の参照渡し

値型の参照渡しについて
それぞれ以下のようになります。
値型  :変数にはを保持している
参照渡し:変数の場所(アドレス)を渡す

つまり、値を保持している変数の場所(アドレス)を渡しているということです。
以下の画像のような経路でxの値を変更しています。

qiita6.png

    // コード抜粋
    static void Main()
    {
        // 構造体のインスタンスを生成(値型)
        SampleStruct samStruct2 = new SampleStruct();
        ValRef(ref samStruct2);
        System.Console.WriteLine("samStruct2.x = " + samStruct2.x); // samStruct2.x = 100
    }

    // 値型の参照渡し
    static void ValRef(ref SampleStruct sam)
    {
        sam.x = 100;
    }

3.まとめ

いかがだったでしょうか?
値型と参照型の違い、値渡しと参照渡しの違いをおわかりいただけましたでしょうか。

私自身が学生時代に「変数は箱」というイメージから脱却できず、C言語のポインタで躓いてしまったので、
少しでもこういった方の一助となればと思い投稿しました。

この記事が少しでもお役に立てていれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambda を C# で書いてみる

はじめに

このドキュメントは AWS Lambda を C# で記述するための手順です。

コードエディットやパブリッシュには Visual Studio を使用します。

開発・実行環境準備

VisualStudio プラグイン

開発者ガイドのAWS Toolkit for Visual Studioを参考に、Visual Studio の拡張機能をインストールします。

プロジェクト作成

Visual Studio の「ファイル」メニューから「新規作成」を選択し、「プロジェクト」を選択します。

「新しいプロジェクト」ウインドウで [AWS Lambda] ツリーを選択し、

「AWS Lambda Project(.NET Core - C#)」 を選択します。

プロジェクト名と保存場所は適宜入力します。

「New AWS Lambda C# Project」ウインドウでは、サンプルアプリケーションのコードを選択することができます。

ここでは、最低限のコードが記述されている [Empty Function] を選択し、「Finish」ボタンをクリックします。

開発とデプロイ

コード記述

このままでも動作するかと思いますが、念のため引数の型を変更します。

//
// 修正前(プロジェクト作成デフォルト状態)
//
    public string FunctionHandler(string input, ILambdaContext context)
    {
        // 引数で受け取った文字列を大文字にして返す
        return input?.ToUpper();
    }
//
// 修正後
//
    public string FunctionHandler(object input, ILambdaContext context)
    {
        // 受け取った引数をログに出力
        context.Logger.LogLine($"Arg : [{input}]");
        // OK という文字列を返す
        return "OK"; //input?.ToUpper();
    }

デプロイ設定

認証情報の準備

Visual Studio から パブリッシュするための認証情報を AWS コンソールから取得します。

AWS コンソール右上のメニューから「ユーザ名▼」を選択し、「マイセキュリティ資格情報」を選択します

[IAM Management Console] (セキュリティ認証情報)画面で、「アクセスキー(アクセスキー IDとシークレットアクセスキー)」を開きます。

「新しいアクセスキーの作成」ボタンをクリックしアクセスキーIDを発行します。

[アクセスキーの作成]ダイアログが表示されたら、[アクセスキーID]と[シークレットアクセスキー]を控えておきます。

デプロイ

Visual Studio のソリューションエクスプローラで、プロジェクトを右クリックし「Publish to AWS Lambda...」を選択します。

[Upload Lambda Function]ダイアログで、「Account profile to use:」のドロップダウン右にあるアイコンをクリックします。

アップロードするためのユーザを追加する画面で以下の項目を入力します。

  • Profile Name:任意
  • Storage Location:Shared Credentials File
  • Access Key ID:認証情報の準備で控えたアクセスキーID
  • Secret Access Key:認証情報の準備で控えたシークレットアクセスキー

入力がおわったら「OK」ボタンをクリックします。

再び[Upload Lambda Function]ダイアログで、「Function Name」に作成する Lambda関数の名前を入力します。
入力したら「Next」ボタンをクリックします。

[Advanced Function Details]ダイアログでは、以下の項目を入力します。

  • Role Name:「New role based on AWS managed policy: AWSLambdaExecute」
  • Memory(MB):「128」

入力がおわったら「Upload」ボタンをクリックします。

これでデプロイは完了です。

テスト

Visual Studio でテスト

デプロイが完了すると、Visual Studio に、作成した Lambda 関数をテストするためのタブが表示されます。

赤い枠のテキストエリアに入力された文字が、Lambda関数の引数として渡されます。

「Example Requetes:」ドロップダウンでは、AWS で接続可能なサービスからの引数サンプルを選択することもできます。

引数の設定が完了したら「Invoke」ボタンをクリックします。

実行結果が、右側のペインに表示されます。また、タブ下部のペインには実行ログが表示されます。

AWS Console でテスト

[AWS Console]から「Lambda」を選択すると、Visual Studio でデプロイした関数が表示されます。

関数名をクリックし詳細画面を表示した後、右上の「テスト」ボタンをクリックします。

[テストイベントの設定]ダイアログが表示されるので、以下の項目を入力します。

  • 新しいテストイベントの作成を選択
  • イベント名:保存するテストパータンの名称
  • 下部テキストボックス:関数に引き渡すテストデータ

入力が終ったら「作成」ボタンをクリックします。

再び「テスト」ボタンをクリックすると関数のテストが実行されます。

テストの実行結果が画面に表示されるので「詳細」をクリックします。

下記キャプチャの、上側の赤枠が関数の戻り値。OKを返していることがわかります。

また、下側の赤枠には関数のログが出力されますが、テストデータとして渡した引数がログに出力されていることが確認できます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SQLServerのデータ型をC#のデータ型に変換する(データ型のマッピング)

SQLServerのデータ型をC#のデータ型に変換する
(SQL Server データ型のマッピング)

対応表

SQL C# キャスト方法
bigint long ToInt64
int long ToInt32
smallint long ToInt16
tinyint long ToByte
real float ToSingle
float double ToDouble

例)
 SQLServerではint型で格納されているデータAを
 VS側で変数に格納したい場合、"ToInt32"を使用する
  int a = Convert.ToInt32(A);

参考資料
『SQL Server データ型のマッピング』
 https://docs.microsoft.com/ja-jp/dotnet/framework/data/adonet/sql-server-data-type-mappings

『C#とSQLServerを繋いでデータを操作する4つの方法【前編】DataTable,SqlDataReader編』
 https://www.udemy.com/course/cs_sqlserver1/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity C#】Color32はstruct!!|すずとも初耳学#1

すずとも初耳学とは

すずともが、プログラミングをするにあたって

  • 知らなかったこと
  • これ便利だなぁーって思ったこと
  • 覚えておきたいこと

などを、「すずとも初耳学」としていろいろ記事書けたらな―と思います。

Color32 は Struct!

#1として取り上げるのは、Unityにある「Color32」という型。

UnityEngine.Color32 - Unity スクリプトリファレンス

最近Textureなどを触る機会があって、少し気になったので調べてみたのですが、

Color32型はStruct!
image.png

はい。それだけです笑

Structだから何って?

C#で構造体を扱おうと思うと、StructとClassがあります。
書き方も似てるしできることも似てるので、初心者は「あれ?同じじゃね?」と思いがちです。(少なくとも僕は)

しかし、StructとClassには明確な違いがあります。それは、

  • Structは値渡し
  • Classは参照渡し

という点です。いやぁこれ結構重要だし、知らなかったら混乱するよね…

Unityでよく使うものを分けてみるとこんな感じ。

  • Struct
    • int型、float型などよく使う基本型
    • Color32
    • Vector2、Vector3
  • Class
    • gameobject
    • transform
    • texture

intとかは何気なく代入してるけど、Structだったからできたのか!
gameobjectのtransformとか参照渡しだから変数で持ってこれたのか!

などなど、個人的には大発見(o^―^o)

というわけで、「Color32はStruct!」w

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む