こんにちは､ そしてお帰りなさい｡ この講義では､ まず文字列関数の話をしたいと思います｡
先ほど､ 文字列､ 文字配列はcの変数データ型ではない､
と書きました｡
だから､ どの演算子でもいいのですが､ やはり文字列に対していろいろなことができるようにする必要があります｡
操作できるようになるんですね｡
長さが欲しくなりますね｡ 比較したいのは､ いろいろなものがおそらく変換されるのでしょう｡
そこで､ 幸運なことに､ 標準ライブラリには､ この種の操作を行うための関数がたくさん用意されています｡
まずは代表的なものからお話しして､ 他の講義でより具体的なものに移っていきます｡
つまり､ 文字列はヌル文字で終端する文字配列であることは既に知っている｡
cプログラムでは､ 文字列がよく使われます｡
幸いなことに､ C言語には文字列を扱うために特別に設計された関数が数多く用意されています｡
これから説明するのは､ よく使うものばかりです｡
まず､ str length関数を使って文字列の長さを取得します｡
ある文字列から別の文字列へコピーする｡
先ほど､ 文字列の代入に代入演算子は使えないと言ったことを思い出してください｡
そこで､ str copyやstrn copyを使うわけです｡
また､ 2つの文字列を組み合わせること､ コンカチネーション（concatenation）｡
str猫やstrn猫がいる｡
また､ 2つの文字列が等しいかどうかを判断すること｡
繰り返しになりますが､ 文字列の比較には､ 配列であるため､ equal演算子は使えません｡
そのため､ 文字列を比較したい場合は､ cライブラリが提供する特殊な関数を使用する必要があります｡
strの比較とstrnの比較｡
まず､ 長さについてです｡
これらの機能はすべて文字列で定義されています｡ ヘッダファイルをインクルードしなければならないので､ これらの関数を呼び出したり起動したりするときには､
ヘッダファイルをインクルードしなければなりません｡
文字列の長さを取得します｡
Str lengthは文字列の長さを求める関数で､ とても簡単です｡
サイズtの変数として返される｡
size t変数はintとほとんど同じように使うことができます｡
でもその前に､ str lengthを使いたいならその文字列のヘッダーファイルをインクルードする必要があり､
その使い方がこれです｡
基本的には二重引用符の中に文字列リテラルを作成し､ それを文字配列に代入します｡
文字配列の大きさを指定しない｡
コンパイラがそれを行い､ NULLターミネータを追加します｡
私の文字列を文字列定数で定義すれば､
あとはstr lengthを呼び出してその文字を渡すだけで､ かなりいい感じです｡
これは最もよく使われるものの1つで､ 検証チェックやバッファオーバーフローがないことを確認するために､
文字列の長さを取得することがよくあるからです｡
この関数は､ 文字列を変更します｡
そのため､ 関数ヘッダでは､ 形式パラメータ文字列の宣言に定数を使用しない｡
もし､ あなたが文字列を見に行くとしたら｡ hをディレクトリのuser
includeの中に入れると､ 定数として定義されていないことがわかると思います｡
文字列をコピーする｡ 文字列のコピーは､ 既存の文字列を変更するか､
何らかのデータを持つ既存の文字配列を変更するかのどちらかです｡
あるいは､ 何も入っていなければ､ 何かを入れたくなる｡
cではraiseを代入できないので､ 文字列も代入できない｡ 先ほども書きましたが､ 文字を宣言して､
等号代入演算子を使ってもうまくいきません｡
そこで､ 既存の文字列に文字列をコピーするために､ strコピー関数を使用する必要があります｡
文字列の演算子､ 代入演算子に相当するものです｡
2つの文字配列sourceとdestinationを宣言すれば､
str copy sourceと言うだけで､ そこにあるsourceは何でも文字列に入れることができ､
destinationも同じです｡
また､ 変数をコピーしたかったら､ 変数も入れることができます｡
しかし､ ここでやっていることは､ 最初のパラメーターがコピー先となることです｡
第2パラメータにはコピーする文字列を指定します｡
つまり､ sourceはstr copyの後にthis is sourceを内包していることになる｡
Destinationまたはdestは､ strコピーの呼び出しの後､ その中にこのdestinationを持つことになる｡
strコピー関数は､ コピー元の文字列がコピー先の文字列に実際に収まるかどうかを確認することはありません｡
では､ 大きな文字列があったらどうなるのでしょう｡
では､ この前の例で､ 宛先が100文字だった場合はどうなるのでしょうか｡
そして､ これを目的地とするのではなく､ 100文字としたのです｡
50文字しかないソースにコピーしてみたんですね｡
問題があるのだろう｡
コピー先の文字列は､ コピー元の文字列より大きくなってしまう｡
ですから､ もしこれをソースとしてコピーする代わりに､ ソースに str コピーしようとしたら､ this is sourced
five times or ten times whatever it is and one over 50 と入れると､
エラーになります｡
そして､ それを実証する｡
そこで､ より安全な文字列のコピー方法として､ strn copyを使用します｡ nはstrnコピーにできることがわかると思います｡
Strn コピーは第3引数を取ります｡
コピーする最大文字数｡
そこで､ ここでは実際にコピーする量を指定します｡
つまり､ ソースが40でデスティネーションが12の場合､ まずmセットをして､
デスティネーション内のデータのサイズをnullターミネーターに設定すれば､
何かを追加するときに自動的にそこに入るので､ 心配はありません｡
strのコピーは､ hello how are you doingをsourceにコピーするつもりで､ how how
are you doingがsourceより大きいかどうかをチェックするつもりはないんだ｡
しかし､ strnコピーをして､ コピー先にコピーすると､ ソースをコピーすることになり､
最初の10文字しかコピーされない｡
だから､ その第3引数はデスティネーションのサイズに対応する必要がある｡
そこで､ コードブロックに入り､ str lengthと文字列のコピーについて見て､ これらがどのように機能するかを正確に見てみましょう｡
では､ ここでメインをご紹介します｡
そして､ 文字列の長さの文字列を得るのは､ 実に簡単です｡
これから文字配列を作成します｡ 私たちはキャラクターエリアの作り方を知っています｡ サイズを指定するのではなく､ まず名前を入れて､
次に括弧を入れるようになったんです｡
そして､ 二重引用符で囲んで､ 好きなように初期化することができます｡
二重引用符は文字列リテラルを表す｡
だから､ 私の名前はジェイソンと言えばいいんです｡
この結果､ sはサイズ17文字の配列になる予定です｡
これらすべての文字にヌル文字を加えたものです｡
NULLターミネータはバックスラッシュゼロである｡
これはコンパイラが自動的に追加してくれるから､ 入れる必要はないんだ｡
以上が､ 文字配列の初期化と文字列の生成方法です｡
そして､ それを印刷するには､ printfと言って長さを言うだけでよいのです｡
そして､ 文字列を表示するための書式指定子として､
パーセントのsを付けています｡
そして､ 文字列の名前を言うだけです｡
そして､ 混乱しないように､ 私の文字列と言いましょう｡
また､ 以前の例のようにアンパサンドを使用する必要はなく､ 単に私の文字列と言えばよいのです｡
これをコンパイルして実行する場合､
文字列ヘッダが含まれていることを確認する必要があるかもしれません｡
これをコンパイルして実行すると､ 文字列の長さが表示されます｡
だから､ 長さはそうだし､ 文字列の長さの関数を呼んでいない｡
だから､ 長さを表示したい場合は実際に関数を呼び出す必要があるので､ そのようなことをすればいいのです｡
str lengthの中に文字列を入れるのです｡
これで､ 実行すると､ 実際に文字列の長さがわかるようになった｡
これは文字列でなく､ 実際のものでなければならない｡
というわけで､ 文字列を表示する方法です｡ 文字列の長さを表示したい場合は､ 関数の呼び出しでパーセントdを行います｡
さて､ これを実行すると､ 実際に長さである16がプリントアウトされることにお気づきでしょう｡
だから､ str lengthを使うのは､ かなり簡単な関数だ｡ 文字列をコピーする場合は､ str
compareを使用します｡
そして､ 実際にバイトを指定するには､ strn compareを使いたい｡
ここで､ 私の名前がJasonであるとすると､
別の文字列を作成します｡
これから行うのは､
tempという別の文字列を作成して､
strと等しくして､ compareに､ コピーしたいものを渡すことです｡
空の文字配列を作り､ それをサイズ50にするだけです｡
そして､ ここで行うことは､ strnコピーと言うことです｡
最初のパラメータはコピーしたい場所､ ここではtempとします｡
2番目のパラメータはコピーしたいもので､ 私の文字列です｡
そして､ 50バイト以上は渡さないようにします｡
ですから､ 実際にはtempのサイズを指定して､ 50バイト以上は渡さないようにすることができます｡
そして､
おそらくヌルターミネータには-1を指定したい｡
このように､ バッファオーバーフローを起こさないためには､ 3つ目のパラメータが本当に重要なのです｡
strn copyではなく､ 常にstrn copyを使用する必要があります｡
そして､ ここで文字列を表示して､ コピーされたかどうかを確認することができます｡
tempと言えばいいのか､ パーセントsに変えればいいのか｡
そして､ これをビルドすると､
長さではなく､ 文字列がこのようなもので､ セミコロンで終わっていることが確認できるはずです｡
さて､ 私たちが行うコンパイルをしましょう｡
そこで今度は､ それを実行するのです｡ コピーされた文字列が表示されるはずです｡
つまり､ 私の名前はJasonで､ tempをプリントアウトしたため､
tempの内部に格納されていることがわかります｡
ただし､ 必ずstrn copyを使用すること｡ str コピーは使わないでください､ そうすればバッファオーバーフローが起きないようになります｡
そして､ 第3のパラメータは常に送信元ではなく､
送信先であるべきです｡
この文字列を丸ごとコピーすると言うのは意味がない｡
これが小さくなると､ 問題が発生しますから｡ だから､ 必ずこの大きさを言ってください｡
そして､ sizeof演算子を使って､ characterの中に何バイト入っているかを見ることができるのがわかりますね｡
もう少し機能を説明しましょう｡
だから､ 長さを求める方法も､ 文字列をコピーする方法も､ 文字列を連結する方法も知っている｡
str cap関数は､ 引数として2つの文字列を受け取り､
最初の文字列の末尾に2番目の文字列をコピーします｡
2番目の文字列は変更されず､ 1番目の文字列だけが変更されます｡
第一引数の値を返します｡
2番目の文字列が追加される文字列の最初の文字のアドレス｡
だから､ もしそれを使う必要があるなら､ 実際に追加した場所を教えてくれるようなものを返してください｡
ここでは例として､ 私の文字列の文字列を作成し､ その中に私の文字列とNULLターミネータを含むだけのものを作成します｡
入力の文字列を作りますが､ これは80で､ 空っぽです｡
連結する文字列を入力すると言うことです｡ で読みました｡
そして､ それを連結するのです｡
元の文字列を出力し､ 次に連結した文字列を出力し､ そして入力を出力する｡
つまり､ この後､ あなたが入力したものは､ 私の文字列の末尾に置かれることになるのです｡
つまり､ ジェイソンを入力すると､ 私のスペース文字列のジェイソンになるわけです｡
それが､ 私の文字列とイコールになる｡
その上でストキャットをやるからです｡
まず文字列をプリントアウトし､ その上でstr catを呼び出す｡
入力は､ 実際には､ 入力されたもの､ 私の文字列､ 入力されたものの後に続くものと同じになります｡
この例については､ 後で説明します｡ さて､ str copyと同じように､
str catにもstrn end catがあります｡
つまり､ 結合されたバージョンは新しい最初の文字列になる｡
つまり､ str catによる文字列連結と同じように､ strn
catがあるのです｡
str catはstr copyと同じ問題があります｡ バッファオーバーフローを起こす可能性がある｡ 実際には､ 2つ目の文字列が最初の配列に収まるかどうかは確認しません｡
そのため､ 最初の配列に十分なスペースがないと､ バッファオーバーフローの問題が発生する可能性があります｡
そこで､ 使いたいのはstrn catです｡
第2引数は追加する文字数の最大値を示す｡
例えば､ strn catと2つの文字列bugs
an add-onを実行した場合､ bugsにadd-onの最初の13文字だけを追加することになります｡
add-onが13未満の場合､
nullのターミネーターが見えるまでしか追加されません｡
つまりこの例では､ 2つの文字列を作成しています｡
これは､ ソースこの宛先です｡ ストルンキャットやってます｡
最後に15文字をdestinationにコピーしているだけです｡
つまり､
宛先のサイズに15を足したものをプリントアウトします｡
では､ これをもう一度コードブロックで見てみましょう｡
ここでは､ サイズ50と50の2つの文字列配列を作成する例を示します｡
データに設定するために､ 2回のstrコピーを行います｡
送信元の宛先です｡ そして､ 12行目でstrnのcatを行っています｡
そして､ destinationは連結されたものになります｡
つまり､ ソースはこれから連結するものになるわけです｡
しかも､ 15文字しか割り当てないんです｡
だから､ もしソースが15より大きかったら､ 全部をコピーすることはないでしょう｡
ビルドして実行すると､ this is
source fileと表示されるか､ this
is destinationと表示され､ this is sourceと表示されることがわかります｡
つまり､ これは目的地､ これはソースと書いてあるわけです｡
それをdestinationの末尾に連結しているのだ｡
だから､ バッファオーバーフローを起こさないように､ 連結には常にstrn catを使いたい｡
str compareについて話そう｡ str copyの代わりにstr compareとうっかり言ってしまいそうだ｡
だから､ 文字列の比較には､ 関数も使わなければならない｡
ある文字列と別の文字列を比較したい場合､ ダブルイコールは使えません｡
それは､ 同じアドレスを持っているかどうかを参照するためにチェックするだけだからです｡
そこで､ 使うべきはstrの比較です｡
これは､ 文字列の内容を比較することになります｡
配列の比較は行いません｡ そのため､ 異なるサイズの配列に格納された文字列の比較に使用することができます｡
キャラクターを比較するものではありません｡
二重引用符で囲んだapplesやaなどの引数は使用できますが､
一文字のaなどの文字引数は使用できません｡
この関数は､ 関係演算子が数値に対して行うことを文字列に対して行うものである｡
つまり､ compare関数は､
文字列が等しいかどうか､ 大きいかどうか､ 小さいかどうかを示す値を返そうとするのです｡
等しい場合はゼロを返すことになる｡
もし､ less thanなら､ str 1がstr 2より小さければ､
0以下を返すことになる｡
そして､ もしstr 2がstr 1より小さければ､ 0より大きい値を返すことになる｡
それで､ 具体的にはどういうことなのでしょうか｡ では､ その例をご紹介しましょう｡
これをコードブロックに入れて実行してみましょう｡
しかしここでは､ たくさんの文字列の比較を行っています｡ 値が何であるか､ より小さいかより大きいかは､ あまり明確ではないので､
教えてあげることにします｡
では､ これをコードブロックにして見てみましょう｡
では､ その例をご紹介しましょう｡ そして､ たくさんの文字列を比較し､ それがゼロイチであろうと何であろうと､
str比較の結果を表示するだけです｡
つまり､ 2つの文字列aとaを比較する場合､ 2重引用符で囲む必要があるのです｡
そして､ ここにあるのは､ 私たちが呼んでいるものをプリントアウトしているところです｡
だから､ 引用を区切っているんです｡
バックスラッシュを入れるのは､
実際に引用符を出力して文字列が終わらないようにするためです｡ しかし､ aとaを比較すると､
これはゼロを返すはずです｡ なぜなら､ ゼロは文字列が等しいことを意味するからです｡
aとbを比較する場合､ str compareはless thanを返すはずです｡
なぜなら､ aはbより小さいからです｡
B aは0より大きい値を返すべきである｡
でも､ これを見ながら､ 間違いがないように､
実際に出力して見ましょう｡
このように､ 実行すると､ どのような数値が返されるかを覚えておくことができます｡
だから､ aとaを比較すると､ 実際には0になる｡
aとbを比べたら､ bはaより小さいから､ マイナスの1､
0より小さいものです｡ 私は逆のことを言ったかもしれませんが､
私でもまだ混乱しているのがわかります｡
aはbより大きいから､
aはbより大きい､ マイナス1｡
Bはaより小さいので､ 0より大きいもの､ つまり1が得られます｡
Cはaより大きいので､ bがaより大きかったときと同じように､
1を得ることができます｡
そして､ 大文字と小文字を比較するわけです｡
大文字と小文字を比較するときは､ その逆を行う｡ Zは､ 実は小文字のaよりも少ないのです｡
つまり､ 大文字は小文字より常に小さいのです｡
リンゴとリンゴを比べようとすると､ リンゴはリンゴより少ないから1になるんです｡
そして､ その比較の仕方はこうだ｡ 1より大きい0や1より小さい0を理解することが重要です｡
ここでもless
than zeroは､ 最初の文字列が2番目の文字列より小さいと言うつもりです｡
そして､ 0より大きいということは､ 最初の文字列が2番目の文字列より大きいと言うことになる｡
この機能は非常に使い勝手が良い｡ 比較する配列の大きさは関係ないので､
strn比較のような心配は不要です｡
str 比較関数は､ 文字列を比較して､ 対応する文字が異なることを見つけるまで比較します｡
だから､ 片方の弦の端までかもしれない｡
Strn比較関数は､ 文字列が異なるか､ 第3引数で指定された文字数を比較するまで､
文字列を比較します｡
ですから､ この場合､ バッファオーバーフローのために strn compare
を使用する必要はありません｡ 存在するのですが､ ある文字数だけ比較したい場合にしか使わないでしょう｡
str copyやstrn
copyやstrn catとは違う使い方をします｡
strnの比較では､ これだけの文字数しか表示したくないと言われるだけです｡
だから､ 接頭辞だけを探すのであれば便利だと思います｡
つまり､ astroで始まる文字列を検索したい場合は､
最初の5文字に限定して､ このようにstrn compareを使用することになります｡
だから､ この例では､ イコールゼロと言うことがわかります｡
str比較をしているときに､ このようなif文をよく見かけます｡
この文の中にstr compareがあり､ equalsがゼロに等しいと言うことですね｡
そこでstrn compareは最初の5文字を使い､ astroとastronomyの最初の5文字を比較します｡
見つけたと言うことになる｡ ゼロに等しくなり､ 天文学を発見したと言うことになる｡
同じことをしても､ astoundingという別の文字列と比較すると､ 見つからないどころか､
astoundingの方がastroよりも大きいと表示されます｡
つまり､
0より大きいもの､ つまり1を表示することになり､ データは表示されないということです｡
ですから､ すべての内容の文字列を比較するときは str compare を使ってください｡
プレフィックスと文字列の比較のみを行いたい場合は strn compare を使用します｡
第3引数のみで比較するstrnと同じタイプの構文です｡
そこで､ これから使う本当に重要な関数､ 主にstr
length､ strn copy､ strn cat､ strn
compareについて､ いろいろと話をしました｡
これらの機能を活用することで､ 身近に感じてもらえるような課題を用意しています｡
でも､ 何度も何度も使っているうちに､ 本当に慣れてくるものなんですよ｡
ありがとうございます｡