20210427のC#に関する記事は5件です。

C# ~マップの判定・縦横~ Aランクレベルアップメニュー

現在働いている会社ではシステムエンジニア研修の1つに「paizaBランク問題をクリア」があるのでその目標を達成するまでの過程を備忘録として残します。 業務に参加した際に記述方法を忘れてしまっているであろう未来の自分に向けて、メモを残すと同時にこの記事がC#を学習している方、同じ箇所で躓いている方の参考になればと思っています。 ですので内容に誤りがある場合やより良い記述をご存知の方はコメントで共有していただけると嬉しいです。 それでは本編に入ります。 今回はPaizaラーニング、Aランクレベルアップ問題集「マップの判定・縦横」の問題をC#で回答したときの内容になります。 STEP: 1 盤面の情報取得 問題文:https://paiza.jp/works/mondai/a_rank_level_up_problems/a_rank_snake_map_step1 paiza.cs using System; class Program { static void Main() { string[] asW_H_N = Console.ReadLine().Split(' '); int iH = int.Parse(asW_H_N[0]); int iW = int.Parse(asW_H_N[1]); int iN = int.Parse(asW_H_N[2]); string[,] a2D_CoordinateData = new string[iH,iW]; for(int i = 0; i < iH; i++){ string as_rowData = Console.ReadLine(); for(var j = 0; j < iW; j++){ a2D_CoordinateData[i,j] = as_rowData.Substring(j,1); } } for(var i = 0; i < iN; i++){ string[] as_coordinateNum = Console.ReadLine().Split(' '); int CoordinateData_Y = int.Parse(as_coordinateNum[0]); int CoordinateData_X = int.Parse(as_coordinateNum[1]); Console.WriteLine(a2D_CoordinateData[CoordinateData_Y,CoordinateData_X]); } } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Twitter風文字カウントを実装してみた

筆者は社内SE業務で、Twitter風の文字超過をハイライトする入力欄を作成した。 その成果をここに共有する。 どうせならQiitaで投稿できるように、徹底的にTwitterを真似てしまえということで、Twitterの文字計測をそのままコードに落とし込んだ(社内SE成果物とは数え方が違うので悪しからず)。 ASP.NET MVCで作成したアプリ経由でTwitterに投稿したい場合には使えるはず(その手のサポートをしてくれる会社のサービスを利用する方が楽だろうが)。ライセンスはMITライセンスなので、自由に使用可能だ。 実装方法 使用したのはASP.NET MVC 5(ASP.NET CoreではTagBuilderの仕様が異なり、ToString(TagRenderMode)が読みだせないようなので、GenerateElementの書き直しが必要)。.Netのバージョンは4.6.1。C#により入力欄と超過部分のテンプレート生成をしている。実際にテンプレートを運用するのはJavaScriptの仕事。ということで責任の重さはC#:JS = 3:7程度である。 実際のコードはGitHubをご覧いただくとして、こちらでは使い方やコードの重要な部分を解説する。 使い方 HTMLヘルパーとしてCharactersCountingDivHelperを登録する。ビューのweb.configのsystem.web.webPages.razor > pages > namespaces要素に<add namespace="(プロジェクト名).(ヘルパーを入れたフォルダ)"/>でヘルパーを登録できる。 BundleConfig.csのRegisterBundles関数にbundles.Add(new ScriptBundle("~/bundles/countInput").Include("~/Scripts/input/count_input.js"));を設定する。 ヘルパーを利用するビューにスクリプトを追加する。宗教的理由で遅延読み込みを使っているので、利用時は<script src="@(BundleTable.Bundles.ResolveBundleUrl("~/bundles/countInput"))" defer></script>と入力する必要がある。これからのJSはdeferが当たり前になるはずなので、今の内に慣れるのが重要だと思う。 フォーム内で@Html.TwitterLikeInputForを使用する。名前がForで終わるのはASP.NET MVC純正のHTMLヘルパーに合わせた形。 @Html.TwitterLikeInputForの引数は5つ。 maxLength(入力欄の最大文字数。半角基準で入力) wrapperHtmlAttributes(ラッパーのHTMLプロパティ。idは指定しても無視される) editorHtmlAttributes(編集箇所のHTMLプロパティ。id、controleditableは指定しても無視される) validationHtmlAttributes(編集箇所のHTMLプロパティ) excessiveStringAttributes(超過部分のHTMLプロパティ) 引数名にAttributesとあるものはいずれもDictionary<string, object>で指定。これはこの手のヘルパーでよくつかわれるObjectだとリフレクションを使わなければならないので、それを避けるという信条の問題がある。名前付き引数で設定すると幸せになれる。 実装の解説 ヘルパー(ASP.NET MVC) オプション引数を使うことでHTML属性の設定を任意に行える。筆者の環境のC#が古いため、属性の初期値設定が面倒なことになっているが、現在はwrapperHtmlAttributes ??= new Dictionary<string, object>();で一発入力できる。ただ、どうせならオプション引数に直接入れられるとNullReferenceException問題が無いのでうれしいのだが。おそらくCLIの変更も必要なので難しいと思われるが、MSには対応していただきたい。 最大文字数も(Twitter風なので意味は無いが)初期値を入力可能にしている。文字数カウントの仕様が独特なためMaxLength属性はあえて使用していない。つまり、文字数のバリデーションは完全にJSの担当である。 後はHtmlHelperの拡張メソッドとTagBuilderを使って各種要素の生成をしているだけである。 編集箇所の方はcontenteditableをtrueにすることで直接編集可能にしている。また、idを指定することで、JSから読み出せるようにしている。さらに、クラスにeditorを追加することで、編集箇所ということを分かりやすくしているので、既存HTMLとCSSにバッティングしないように注意! 属性を直接指定できる要素の他にも、文字数カウンターも生成している。 JavaScript Visual Studio 2015の非力かつ時代遅れ(例えばfor (const foo of bar)に対応していない!)のIntellisense機能を利用してのJSコーディングは疲れたぜ。 大発見。hogeもしくはpiyoのどちらかで分割させて、なおかつ両方とも文字列として残したい場合、正規表現はこう書く。 ((?:hoge)|(?:piyo)) この?:が無いと、例えばhogeがマッチングした際にpiyoのマッチングも検証され、見つからないためnull配列が生成されてしまう(逆も然り。説明が不正確なのはご容赦を)。今回最大のハマりポイント!ネットにも情報が転がっていなかったので、ちょっと自慢したい。 (2021/4/28追記)当該コードを(hoge|piyo)と書いても全く問題ないことに気づくが、(?:)を付けることで区切りが分かりやすくなる、ということでどうだろうか。 本来分割文字(CJK文字(ひらがな、カタカナ、漢字、ハングル)および空白文字)の正規表現はCJK文字の正規表現と結合したかったが、うまくいかずに繰り返して書いている。無念。 なお、後読み(?<=hoge)を使えればURLの検出が簡単になった1のだが、さすがにSafariの対応はしておいた方がいいなというのと、Twitter自身がしていることと違うだろうということで没に。 正規表現でその他注目なのは、[\p{Script=Katakana}]でUnicodeカタカナの文字グループを一気に読みだしているところ。SafariやChromeが対応し、V8の正規表現エンジンをFireFoxが導入したことで、ほとんどのWebブラウザがこの記法を使用可能となり、IE(と旧Edge)の互換性と引き換えに[\u]で直接文字コードを指定する手間がかなり削減されたうえ、正規表現が分かりやすくなった。ただし、句読点などは文字グループが見つからなかったため直接指定している。 inputイベントのisComposingがtrueかどうかを調べることで、日本語や中国語を入力時は変換確定後、アルファベットなどは入力ごとに文字数チェックが走るようにしている。 文字数カウントは「CJK文字」(1文字につき1)「URL」(長さにかかわらず11.5文字固定)「それ以外」(1文字につき0.5)でカウント方法を分けている。内部的には整数で扱いたいため、倍でカウントしている(おそらくTwitterもそういう風に実装している)。 最大文字数を超過した場合、文字列を分割するメソッドを走らせる。文字列を分割文字で配列に分けて、配列ごとの文字を数え、通常の文字列に追加していく。最大文字数を超過する部分に遭遇したらそこで分割位置を計算(最大文字数と直前の文字数の差が分割位置になる)、分割位置よりも前は通常の文字列、それ以降は超過文字列に分ける。以降の配列はすべて超過文字列に投入する。最後に、通常の文字列は編集箇所のテキストノードに、超過文字列は超過部分用の<span>に入れ、カウンターを赤字にする。 サブミット時に、編集箇所の内容をinputに代入するのもJSの責務だ。編集箇所をgetElementsByClassNameで抽出した後、超過部分のspanが存在しない編集箇所のtextContentをinputのvalueに入れる。超過部分が存在する場合はエラーを出すようにする。 反省箇所 文字カウントはCJK文字以外の文字数をベースにしている。その方が処理が分かりやすいと思ったが、よくよく考えれば直感的ではなかった。変更はリスクが高いので、今更引き返せない。 HTMLの要素生成にTagBuilderを利用しているが、HTML要素を直接入れ子にできない2ので扱いが難しい。AngleSharpを勉強して活用したほうが後々のためになったような気がする。 要素全体を囲むラッパーにidを指定しているが、よくよく考えたらラッパーで何かするというのも思いつかないし、余計な機能だった気がする。作った時に何を考えてたんだろう、私… 編集箇所のクラス名を、もう少し凝った、被りづらい名前にすべきだったと思う。 カウンターのHTML属性を指定できた方が良かった。 と挙げてみたところ、反省箇所はおおむね設計の問題に集中している。コード自体はパフォーマンスの問題(例えば文字カウントを超過判定・仕分けの2回行っている)など思うところはあるが、ほぼほぼクリーンに書けていると思う。問題点があったらIssuesやプルリクの投稿をお願いします。 まとめ 実装したい機能の概略は割とシンプルだったが、特にJSは考えさせられる場面が多々あり、文字カウントの方法や正規表現は難しかった。この文字カウントを実装したTwitterのエンジニアは本当に頭がいいと思った。 コードを実際に読まずに、与えられた仕様で実装するのは、エンジニアの地頭を鍛えるエクササイズとしては良いものだった。 惜しむらくは、後先考えずに設計をおろそかにしてしまったため、使いづらそうな部分が多いということか。問題点があったらIssuesやプルリクの投稿をお願いします(大事なことなので2回言いました)。 皆さんも、自分のTwitter風文字カウントを作ってほしいと思う。 参考 コーディング及び記事作成時に参考にしたサイトを挙げる。 Twitter文字数カウント5ルール | URLや改行は何文字? - 作業ロケット javascript - How can I use the script defer attribute for ASP MVC 4 Bundles with Scripts.Render - Stack Overflow 正規表現 | 先読みと後読みを使ったパターン - Let'sプログラミング 正規表現 \p{...} メモ - Qiita [javascript]URLを取得する正規表現 - Qiita Mozilla、今後はV8の正規表現エンジンをFirefoxにそのまま取り込むと表明。そのための互換レイヤを開発 - Publickey 履歴 (2021/4/27追記)後読みを利用した正規表現の式がおかしかったので修正。 (2021/4/28追記)正規表現の書き方で判明した点があったので修正。 文頭にURLが来るか、CJK文字もしくは空白の直後にURLが来るというのを、後読みを利用して書くとこうなる。/(?<=^|[\p{Script=Katakana}\p{Script=Hiragana}\p{Script=Han}\p{Script=Hangul}\u{3000}-\u{303F}\u{FE30}-\u{FE4F}]+|\s+)(https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:@&=+\$,%#]+)/gu ↩ 入れ子にしたい内容を一旦テキストに変換して、入れたいtagBuilderのInnerHtmlに代入するか、開始タグ・終了タグを別個に生成して入れ子にしたい内容をテキスト化する際に挟み込む。今回は後者を採用している。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】DataTable でのLINQの使い方(1)

はじめに C#のDataTableクラスでLINQを使ったコードを書く際に、暫くコードを書かないと、LINQは複雑な手続きの為、何回も調べ直さなければならないことがありました。これを防ぐ為に簡単にまとめた内容を記載します。 複数列の取得 DataTable から必要な項目のみを取得 ※あまり使う機会はないかもしれないが… DataTable member = new DataTable(); member.Columns.Add("No", typeof(int)); member.Columns.Add("Name", typeof(string)); member.Columns.Add("Age", typeof(int)); member.Rows.Add(1, "太郎", 20); member.Rows.Add(2, "次郎", 15); member.Rows.Add(3, "三郎", 10); var datas = member.AsEnumerable() .Select(x => new { Name = x["Name"], Age = x["Age"] }); foreach (var data in datas) Console.WriteLine("Name:{0},Age:{1}", data.Name, data.Age); 簡単な説明 Selectにnew演算子をセット後、キー名及び値となるDataTableの項目(列の名前)をセットします。 複数ある場合はカンマで区切ります。 ”x =>”の x は何でも良かったりします。 出力結果 Name:太郎,Age:20 Name:次郎,Age:15 Name:三郎,Age:10 条件に合ったデータの取得 DataTable から条件に合ったレコードを取得 ※ループやif文条件を使うよりは、SQLに慣れているとこちらの方が見やすいかな… DataTable member = new DataTable(); member.Columns.Add("No", typeof(int)); member.Columns.Add("Name", typeof(string)); member.Columns.Add("Age", typeof(int)); member.Rows.Add(1, "太郎", 20); member.Rows.Add(2, "次郎", 15); member.Rows.Add(3, "三郎", 10); // rowsで結果を取得 DataRow[] rows = member.AsEnumerable() .Where(x => (int)x["Age"] > 10) .ToArray(); Console.WriteLine("rowsで結果を取得"); foreach (DataRow row1 in rows) Console.WriteLine("No:{0},Name:{1},Age:{2}", row1["No"], row1["Name"], row1["Age"]); // dataTableで結果を取得 DataTable dataTable = member.AsEnumerable() .Where(x => (int)x["Age"] > 10) .CopyToDataTable(); Console.WriteLine(""); Console.WriteLine("dataTableで結果を取得"); foreach (DataRow row2 in dataTable.Rows) Console.WriteLine("No:{0},Name:{1},Age:{2}", row2["No"], row2["Name"], row2["Age"]); 簡単な説明 Whereに取得条件をセットします。 結果を格納する変数の型を2パターン記述してみました。 出力結果 rowsで結果を取得 No:1,Name:太郎,Age:20 No:2,Name:次郎,Age:15 dataTableで結果を取得 No:1,Name:太郎,Age:20 No:2,Name:次郎,Age:15 おわりに LINQは何かと使い勝手が良いと思います。 ただ、暫くコードを書いていないと、ふと書き方を忘れてしまうなと感じております。 この記事は備忘録的な意味合いが強く、詳しい説明などは記載しておりません。 「こんな事が出来るんだ」程度に見て頂ければ幸いです。 今回は初回として、記事としてはここまでになります。 今後、内容を追記していければと考えております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ゲームでよくあるアイテムが手前に来るやつ

ゲームでよくあるやつ 先日、推しが配信で「フードデリバリーサービス」というゲームをプレイしていた。 そのゲームの以下のような描写を見て「作れそう」と思ったので作りました。 ソースコード LookingItemBehaviour.cs using UnityEngine; public class LookingItemBehaviour : MonoBehaviour { [SerializeField] private Transform target; [SerializeField] private float moveDuration = 2f; private float moveProgress = 0f; private bool isNeedMove = false; private bool isNeedReturn = false; private Vector3 startPosition; private Quaternion startRotation; void Start() { startPosition = transform.position; startRotation = transform.rotation; } void Update() { // Test if (Input.GetKeyDown(KeyCode.Return)) StartMove(); if (Input.GetKeyDown(KeyCode.Space)) StartReturn(); if (isNeedMove) { moveProgress += Time.deltaTime / moveDuration; if (moveProgress >= 1f) moveProgress = 1f; transform.position = Vector3.Lerp(startPosition, target.position, moveProgress); transform.rotation = Quaternion.Lerp(startRotation, target.rotation, moveProgress); } else if(isNeedReturn) { moveProgress -= Time.deltaTime / moveDuration; if (moveProgress <= 0f) moveProgress = 0f; transform.position = Vector3.Lerp(startPosition, target.position, moveProgress); transform.rotation = Quaternion.Lerp(startRotation, target.rotation, moveProgress); } } public void StartMove() { isNeedMove = true; isNeedReturn = false; } public void StartReturn() { isNeedReturn = true; isNeedMove = false; } } 仕組みはいたって単純 ターゲットを指定してそのターゲットと同じpositionとrotationに指定時間で変化するというもの。 動作テスト SmartPhoneというオブジェクトにLookingItemBehaviourをアタッチしています。 シーンビューの赤い球がtargetに指定してあるItemPositionの位置です。 テスト用にEnterキーで手前に、Spaceキーで元の場所に戻るようコード内に記述をしています。 以下、動作している様子 うごいたー 今後の改良 このままではゲームに組み込みづらいので移動終了時のイベントなどを追加してもっと汎用的に使えるよう改良しようと思います。いつか
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# の Action型の色々な書き方メモ

これは何? C# の定義済デリゲート型である Action型の処理を定義する際に、今のC#ならラムダ式でサクって書けてしまえるんでめちゃ便利なわけですが、いつも「これって本来どんな書き方なんやっけ」ってつい気になって寄り道してしまうことを繰り返してしまうんで、将来の自分に向けたメモ書き。 引数を持たない Action型 // 「定義済のint型変数の値をインクリメントする」だけの処理をAction型変数に代入する var num = 0; // 1.delegateキーワードを使った古典的な書き方 Action action1 = delegate () { num++; }; // 2. var を使った書き方 var action2 = new Action(delegate () { num++; }); // 3. ラムダ式で書く Action action3 = () => num++; // 4. var を使った書き方 var action4 = new Action(() => num++); まあ、3.の書き方が一番短く書けるんやけど、自分的には var って書きたいんで 4. の書き方がいいかなー。 引数を1つ持つ Action型 // 例として「int型の引数の値をインクリメントする」だけの処理をAction<int>型変数に代入する // 1.delegateキーワードを使った古典的な書き方 Action<int> action5 = delegate (int num) { num++; }; // 2. var を使った書き方 var action6 = new Action<int>(delegate (int num) { num++; }); // 3. ラムダ式で書く Action<int> action7 = num => num++; // 4. var を使った書き方 var action8 = new Action<int>(num => num++); 引数なしの時とまぁ、同じですね。 3. の書き方は引数のカッコを外せてしまうので、やっぱりちょっと違和感を感じてしまうのは僕だけでしょうか。 あとFunc型についても書こうかと思ったけど、まぁこれもほぼ同じですので省略します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む