C言語メモ
c言語は昨年2年次のアルゴリズムとデータ構造の講義で少し触れたが,理解しようとしていなかったためこれを機に学びなおすことにした.ファイルの取り扱いがメインになると思うが,メモ程度に書き記していく.
char ex[32];
scanf
文字列の入力
char ex[32];
scanf("%s", ex );
数値の入力(数値の場合引数は数値のアドレス)
int ex;
scanf("%d", &ex);
複数入力
char ex[32];
int exp;
scanf("%s %d", ex, exp);
戻り値は入力個数
ファイル入出力
char infile[1024];
FILE* infp;
scanf("%s", infile);
infp = fopen(infile, "r");
scanfで読み込むファイル(ex:sample.txt)を入力(ディレクトリは同じ必要がある)
するとinfileに読み込んだファイルが保存infileを出力するとsample.txtが出力
fopenでファイルを開くことができる.
infpを出力するとそのアドレスを出力する.
fgets
1行単位で読み込む
第一引数:保存先配列(ex:char outfile[1024])
第二引数: 読み取る文字数
第三引数: ファイルポインタ(このアドレスのファイルを読み取る)
char infile[1024];
char outfile[1024];
FILE* infp;
scanf("%s", infile);
infp = fopen(infile, "r");
while(fgets(outfile, 1024, infp)){
printf("%s", outfile);
}
この出力は読み込んだファイルの内容になる.例えばsample.txtを読み込んだとしてsample.txtの内容がai am saito asuka なら出力はai am saito asukaになる.
1回のループで1行読み込むことができる.ループを用いているのでforで書き表すこともできる.その場合は以下のようになる.
int readLength = 10;
for(int i = 0; i <readLength; i ++){
fgets(outfile, 1024, infp);
printf("%s\n", outfile);
}
readLengthの値によって読み込む行数が変わる.読み込む行数がわかってる場合はこちらも有効な方法である.
fputs
1行単位で書き込む
第一引数:文字列
第二引数: ファイルポインタ(このアドレスのファイルに書き込む)
#include<stdio.h>#include<stdio.h>
int main(){
char infile[1024];
char outfile[1024];
FILE* infp;
FILE* outfp;
scanf("%s", infile);
scanf("%s", outfile);
infp = fopen(infile, "r");
outfp = fopen(outfile, "w");
while(fgets(outfile, 1024, infp))
{
printf("%s", outfile); fputs(outfile, outfp);
}
fclose(infp);
fclose(outfp);
}
これで複数行の入力を任意のファイルに書き込むことができる.
fscanf
fscanfはスペースで区切られるまでを読み込んでくれる.
第一引数:ファイルポインタ(このアドレスのファイルに書き込む)
第二引数:型の指定
第三引数:代入先のアドレス
char testdata[8];
int testInt;
printf("読み込みファイル指定");
scanf("%s", infile);
infp = fopen(infile, "r");
fscanf(infp, "%s", testdata);
fscanf(infp, "%d" ,&testInt);
第三引数はInt型などの場合は&をつけないとアドレス参照できないので注意.
for(int i = 0; i <10; i ++){
for(int j = 0; j <10; j++){
fscanf(infp, "%d", &images.image[i][j]);
}
}
ループを回すことでたくさんのデータを読み込むことができる.
fprintf
第一引数:ファイルポインタ
第二引数:型
第三引数:第二引数で指定した型の変数
(第二引数の型を増やすことで,その型にあった変数を第四引数以降に指定して書き込むこともできる.)
char testdata[8];
int testInt;
printf("読み込みファイル指定");
scanf("%s", infile);
infp = fopen(infile, "r");
fscanf(infp, "%s", testdata);
fscanf(infp, "%d" ,&testInt);
printf("書き込みファイルを指定");
scanf("%s", outfile);
outfp = fopen(outfile, "w");
fprintf(outfp,"%s\n", testdata);
fprintf(outfp, "%d", testInt);
fclose(infp);
fclose(outfp);
fprintfの第三引数はfscanfとは違い直接変数を入れればよい
getchar
標準入力から1文字を受け取る.入力した一文字が戻り値となる.
getcharを回すことで複数文字入力が可能となる.
while *1 != EOF){}であったり
void getline(char s, int lim){
int c, i;
for(int i = 0; i< lim-1 && (c = getchar()) != '\n'; i++){
s[i] = c;
}
s[i ] = '\0';
}
getcharは一文字入力を受け付けるのにも関わらず複数入力を可能としてしまっている.もし複数入力をした場合はあふれた文字をストックしておいて自動でそれを入力として取得できる.ENTERで入力を終了するが最後のエンターは改行コードとして最後にストックされる.
詳しくは以下の引用を参考に…
getcharについて
http://web.wakayama-u.ac.jp/~takehiko/honkarac/elde/inputstream.htm
putchar
一文字の書き込み.getcharで読み込みを行ったらputcharで書き込む.
char write_str = "ABCDEFG12345あいうえお";
for ( i = 0; i < sizeof(write_str) - 1; i++ ) {
putchar(write_str[i]);
}
putchar('\n');
isalpha
文字がアルファベットかどうかを判定してくれる.
#include<ctype.h>
includeする必要がある.
引数は文字.
戻り値はアルファベットなら真それ以外なら偽が返る.
isdigit
文字が10進数の数字かどうかを判定してくれる.
引数は文字.
戻り値は数字なら真それ以外なら偽が返る
isalnum
アルファベットか数字かを判定してくれる.アルファベットか数字なら真,それ以外なら偽を返す
ungetc
読み込んだ文字をファイルポインタに1文字押し戻す.
第一引数:押し戻したい文字
第二引数:ポインタ
戻り値:成功なら押し戻した文字,失敗ならEOF
*1:charData = getchar(
実験メモ
コマンドライン引数とは
通常のメイン関数とは異なった書き方.亜種.
引数を二つ持っていてargcはコマンドライン引数の数+1が格納されいてる.argvは文字列の先頭アドレスが格納されている.
コマンドライン引数を用いることで今まで複数回にわたって行っていた入力を一度に行うことができる.
open()
ファイル記述とはシステム全体のオープン中のファイルのテーブルのエントリである.
このエントリはファイル・オフセットとファイル状態フラグを保持する.ファイルディスクリプタはこれらのエントリの一つへの参照である.
ファイルディスクリプタはint型である
通常、 0:標準入力1:標準出力2:標準エラー出力の3つはOSが最初に用意する.よってプログラムがファイルをオープンすると3から順番にディスクリプタが割り当てられる.
fopen(path, "r") |
open(file, O_RDONLY) |
fopen(path, "r+") |
open(file, O_RDWR) |
fopen(path, "w") |
open(file, O_WRONLY|O_CREAT|O_TRUNC, 0666) |
fopen(path, "w+") |
open(file, O_RDWD|O_CREAT|O_TRUNC, 0666) |
fopen(path, "a") |
open(file, O_WRONLY|O_CREAT|O_APPEND, 0666) |
fopen(path, "a+") |
open(file, O_RDWR|O_CREAT|O_APPEND, 0666) |
Openとfopenの対応表
fget(),fput()
Fget()はストリームから1行を取り込むものである.
Char *fgets(char *s, int n, FILE *stream)
Fgetsで文字列を読み込み,読み込んだ文字列をfputsで書きこみをしている
Fputはファイルに文字列を書き込むものである.
第一引数に書き込む文字列,第二引数にはファイルポインタを指定する.
fread, fwrite
Fwrite:引数は4つある.第一引数 const void *buf:書き込みデータのポインタ
第二引数 size_tsize:書き込みデータのバイトの長さ
第三引数 size_tn:kakikomide-ta :書き込みデータの数
第四引数 FILE *fp:Fileポインタ
戻り値は書き込んだデータの個数が返ってくる
Fread :引数は4つある.第一引数 const void *buf:読み込みデータ格納先のポインタ
第二引数 size_tsize:読み込みデータのバイトの長さ
第三引数 size_tn:読み込みデータ数
第四引数 FILE *fp:FILEポインタ
戻り値は読み込んだデータの個数が返ってくる
バッファサイズはデータを一時的にためておくところ
ネットワークプログラミング2
マルチクライアント対応
通常のサーバプログラムは複数のクライアントからの同時接続を受け付ける必要がある.
シングルスレッドや同期I/OブロッキングI/O方式では1クライアントからの接続のみしか受け付けることはできない.
マルチクライアント対応の手法
シングルプロセスシングルスレッド環境でノンブロッキングI/Oや非同期I/Oを利用
利点 リソース消費が少ない 送受信データの連携が容易 欠点時間を要するとき並列性を確保しにくい,プログラム異常時にプログラム全体が影響を受ける1プロセスのリソースに制約がある.
ブロッキングI/O
データ入出力を行う際にアプリケーション側のほかの処理をブロックしてデータ入出力を進める方式
ex write関数で書き込み不可能な場合書き込みが可能になるまで待つ
ノンブロッキングI/O
データ入出力を行う際にアプリケーション側のほかの処理をブロックせずにデータ入出力処理を進める方式
同期I/O
I/Oとの入出力処理をユーザプログラムと同期して行う方式,つまりユーザプログラムのリクエストに対してKarnelが応答するという方式
非同期I/O
I/Oとの入出力処理をユーザプログラムとは非同期で行う方式,ユーザプログラムは入出力が可能になった段階でKarnel側から通知を受けて入出力を行うことができる.
ex O_NONBLOCKフラグを設定,そしてopenしたファイルをファイルディスクリプタに対するwrite関数が書き込み不可能ならば,即エラーを返す
ブロッキングI/Oで同期I/Oならばreadwrite関数
ノンブロッキングI/Oで同期I/Oならばノンブロックフラグでオープンしたrreadwrite関数
ブロッキングI/Oで非同期I/OならばIOmultiplexingでselectやpollなどの関数がある
ブロッキングI/Oで同期I/OならばAIOでepollやkqueueなどがある
ブロッキングI/Oで非同期I/O
select関数を呼び出した時点で待ち状態が発生
複数のIOを同時に扱えるのでKarnelに対して複数のIOのをだす.そして読み込み可能になったIOに対して読み込みのリクエストを行い読み込む
ユーザアプリケーションとカーネルは非同期
Selectについて
基本的には複数のIOを対象にしていていずれかが入出力可能になるまで待ち可能なったらIOに対して処理を行う方式
使い方
マスクの初期化
チェックしたいIOを登録
ノンブロッキングI/Oで同期I/O
読み込み不可なら即エラーを返す
読み込み不可なら即エラーを返す
読み込み不可なら即エラーを返す
可能になったら読み込む
よって即エラーを返すので待ち状態は発生しない.
がアプリケーションからのリクエストに対して応答するのでアプリケーションとカーネルが同期しながら処理を進める
ブロッキングI/Oで同期I/O
AIO
最初にアプリケーションプログラムがKarnelに対して監視するIOを登録そのIOが読み込み可能になった時点でカーネルがアプリケーションに通知をしてアプリケーションはIO処理を進める.通知がくるまでの間アプリケーションはノンブロッキングなので他の処理を進めることができる
Unityで2Dゲームを作る~Part2~
Unityで2Dゲームを作る~Part2~
前回は敵を動かすところまで作れたので今回は敵の動きを複数作れるように改良するところから始める。
~敵の動きを複数作る(難)~
筆者の理解も6割弱である。
waveconfigというScriptファイルを作る
今まで
public class class名 : MonoBehavior{}
今回
public class class名 : ScriptableObject{}
上記のように今回はクラス名の横に付随している(なんて言うのかわからない)MonoBehaviorを用いない。
今回用いるのはScriptableObjectである
https://docs.unity3d.com/ja/2018.4/ScriptReference/ScriptableObject.html
詳細は上記URLを確認してほしい。
このScriptableObjectはオブジェクトに付加する必要のないゲームオブジェクトを作成する場合にこのクラスが使われる。
今回はこのクラス利用して、
GameObject enemyPrefab;
GameObject pathPrefab;
float timeBetweenSpawns = 0.5f;
float spawnRandomFactor = 0.3f;
int numberOfEnemies = 5;
float moveSpeed
これらのゲッター
とセッターを用意した。(もしかして[SerializeField]ってpublicの上位互換的なイメージがあったけどprivate的側面がある?)
先にこれらのゲッターとセッターを用意したと述べたが、pathPrefabについてはpathだけでなく、それぞれの地点Waypointsも必要とするので、リスト型でwaveWaypointsなるものを用意して、それをforeachで回しAddでchildをListに加える。(Listは配列と違い後から加えることができる。)
*childというのはforeach(Transform child in pathPrefab.transform)で宣言されていて、WaypointsがPathの子であることからchildの名前にしてある。
*foreach
https://www.sejuku.net/blog/41892
配列やList,Dictionaryなどの要素にアクセスするときに使うことができる。
使い方
- foreach(型名 オブジェクト名 in コレクション) {
- 処理文
- }
例)
- using System;
- namespace Sample
- {
- class Sample
- {
- static void Main()
- {
- char[] src = new char[5]{'a', 'b', 'c', 'd', 'e'};
- foreach(char chr in src) {
- Console.WriteLine("{0}", chr);
- }
- Console.ReadKey();
- }
- }
- }
結果
a
b
c
d
e
[CreatAssetMenu(menuName = "")]
このGameObjectをcreat→の欄から使用するためにこれで名前を付ける必要がある。
これらの準備ができたら、EnemyPathing.csのプログラムを改変する。
いままではwaypointは[SerializeField]で外部から値を変更していたが先のプログラムでその必要がなくなった。
そのため[SerializeField]を削除する。
次にWaveconfigをこのEnemyPathing.csからアクセスしたいので、アクセスできるように宣言する。
([SerializeField] WaveConfig waveConfig)
そしてwaypointsをwaveconfigのGetWayPointにすることで、敵の移動の生成が用意になった。
具体的にどのような流れになるかといえば、
Node.jsで集計処理
csv: comma separated values カンマで分けられた値.
ファイルの読み取り.
require関数で使いたいライブラリ名を指定してあげると使うことができる.
よってrequire('fs')
fs: filesystemモジュール
ファイルの書き込みや読み込みに使う
readline:それをどのように読み込むかというモジュール.このreadlineは1行ずつ読むというもの
ストリーム(stream):入出力が発生する処理をstreamで扱う.英語の意味が流れであるように情報の流れに注目する.
createReadStream:ここでcsvファイルを引数に入れて読み込む.
rs = fs.createReadStream('csvファイル')とかにする
createInterface:第一引数のinputにrsを入れる.第二引数のoutputは{}としておく
rl = readline.crateInterface({'input': rs, 'output': { } })
イベントを引き起こす
rl.on('line', (関数の引数) => {
関数の中身
})
'line'は一行読むたびに実行してくださいの意.
split: splitの引数にどの文字で区切るかを設定してあげると、一つの文字列がその設定した引数によって複数に区切られる.
parseInt():入れた引数を数字に変換することができる
配列の添え字が0,1,2....などの数字であったが連想配列は添え字を好きな文字に設定することができる.
var 変数名 = new Map()
でMapのオブジェクトを作り,変数名.set(’添え字’,'value')でデータを追加する.参照したいときは変数名.get(’設定した添え字’)
for of構文
for(let [key, value] of ~~ ){}
連想配列などの中身をofの前に与えられた変数に代入してforループと同じことができる.こうすることで添え字が不要で配列の要素だけを使いたい場合に便利
Array.from
連想配列を普通の配列に変換してくれる
添え字も要素化している?
Sort
並べ替えをしてくれる関数
関数を引数にとる
ネットワークプログラミング1
~Socketとは~
ネットワークプログラミングに必要となるAPI
BSDSocketが開発されて以降さまざまな実装が行われた.
ソケットライブラリはトランスポート層以下の機能をサポートする.
したがってこのライブラリを利用することで,新しく開発する際はアプリケーション層,プレゼンテーション層,セッション層の開発を行えばよい.(これはTCP/IPモデルにおいても同じである)
~ソケット通信の流れ(TCP/IP)~
サーバ側
ソケット(通信ポート)を作成する
socket(int domain, int type, int protocol);
通信相手がソケットを識別するためのアドレスを付与
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int sockfd:
先に生成したsocketのディスクリ プタ
const struct sockaddr *addr: 構造体,アドレスを付与する
ipを利用する場合はIPアドレスとポート番号を付与する.
接続要求の待受準備を行う
int listen(int sockfd, int backlog);
確立要求がきたら接続の確立応答を返す
int accept(int sockfd, struct sockaddr *addr, socklen_t *addlen);
確立したコネクションに対する新しいファイル記述子を与える
int sockfd:コネクション要求待ちに用意されたソケット
struct sockaddr *addr:接続されたクライアントの情報
データの転送
レシーブ関数で相手からの通信を待ち受け取ったデータを用いてアプリケーションを処理する
最初に作成したソケットでななくacceptで受け取ったソケットを利用して送受信する
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
size_t send(int sockfd, const void *buf, size_t len, int flags);
終了処理
closeでコネクションを切断しソケットを開放する. 基本的にはこれを使う
shutdownはコネクションの送信受信の機能は終了するがソケットは開放しないので別途closeする必要がある
int close(int fd);
int shutdown(int sockfd, int now);
クライアント側
ソケット(通信ポート)を作成する
socket(int domain, int type, int protocol);
コネクションの確立要求を出す
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
const struct sockaddr *addr:度のアドレスに確立要求を出すかの引数
データの転送
レシーブ関数で相手からの通信を待ち受け取ったデータを用いてアプリケーションを処理する
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
size_t send(int sockfd, const void *buf, size_t len, int flags);
最初に作成したソケットを利用して送受信を行う
終了処理
closeでコネクションを切断しソケットを開放する. 基本的にはこれを使う
shutdownはコネクションの送信受信の機能は終了するがソケットは開放しないので別途closeする必要がある
int close(int fd);
int shutdown(int sockfd, int now);
~ソケット通信の流れ(UDP/IP)~
サーバ側
ソケット(通信ポート)を作成する
socket(int domain, int type, int protocol);
通信相手がソケットを識別するためのアドレスを付与
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int sockfd:
先に生成したsocketのディスクリ プタ
const struct sockaddr *addr: 構造体,アドレスを付与する
ipを利用する場合はIPアドレスとポート番号を付与する.
コネクションの確立が必要ない!
データ転送
この時点で通信相手が指定されていないので通信相手を指定しメッセージを送ることになる
ssize_t sendto(int sockfd, const void *buf size_t len, int flags, const struct sockaddr *dest addr, socklen_t addlen);
ssize_t recvfrom (int sockfd, void *buf size_t len, int flags, struct sockaddr *dest addr, socklen_t addlen);
終了処理
closeでコネクションを切断しソケットを開放する. 基本的にはこれを使う
shutdownはコネクションの送信受信の機能は終了するがソケットは開放しないので別途closeする必要がある
int close(int fd);
int shutdown(int sockfd, int now);
クライアント側
ソケット(通信ポート)を作成する
socket(int domain, int type, int protocol);
通信相手がソケットを識別するためのアドレスを付与(TCPと異なる!)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int sockfd:
先に生成したsocketのディスクリ プタ
const struct sockaddr *addr: 構造体,アドレスを付与する
ipを利用する場合はIPアドレスとポート番号を付与する.
コネクションの確立が必要ない!
データ転送
この時点で通信相手が指定されていないので通信相手を指定しメッセージを送ることになる
ssize_t sendto(int sockfd, const void *buf size_t len, int flags, const struct sockaddr *dest addr, socklen_t addlen);
ssize_t recvfrom (int sockfd, void *buf size_t len, int flags, struct sockaddr *dest addr, socklen_t addlen);
終了処理
closeでコネクションを切断しソケットを開放する. 基本的にはこれを使う
shutdownはコネクションの送信受信の機能は終了するがソケットは開放しないので別途closeする必要がある
int close(int fd);
int shutdown(int sockfd, int now);
~sockaddr構造体~
任意のプロトコルに対応するために一般化して設計しているため直接使われることはあまりない
格納するデータは直接操作せずにgetAddInfoなどのAPIを利用して操作する
ストリームとしてデータを扱う
クライアントで100バイトのデータを2回に分けておくった場合にサーバでは200バイトのデータとして受け取られる
getAddInfoではTCP/IPを利用するためにSOCK_STREAMを指定することで即した通信が行われる
UDP/IP
データグラムとしてデータを扱う
クライアントで100バイトのデータを2回に分けておくった場合にサーバでは100バイトのデータがふたつとして受け取られる
getAddInfoではTCP/IPを利用するためにSOCK_DGRAMを指定することで即した通信が行われる