こんにちは､ そしてお帰りなさい｡ またもや刺激的な講演会です｡ この講義では､ 文字列と文字列関数について詳しく説明します｡
主に､ 文字列を検索してトークン化し､ 解析する機能について説明します｡
では､ もう少し文字列の関数について説明しましょう｡ 文字列を検索する｡
文字列を検索する｡
これらの関数はすべて､ 文字列のヘッダーに入ることになる｡
特定の文字や部分文字列を検索するような､ 特殊な使い方をする文字列検索関数があります｡
これらの関数の名前は､ strchrとstrstrである｡
文字列の中の1文字または部分文字列を検索する｡
その例をいくつかご紹介します｡
また､ 文字列をトークン化する機能もある｡
文字列をトークン化するとは何事か､ と言われるかもしれません｡ まず､ トークンとは何かを定義する必要があります｡
トークンとは､ 文字列の中でデリミターによって区切られた文字の並びのことです｡
デリミタとは､ 文字列の中にあって､ 特別な意味を持つことに気がつくものです｡
つまり､ トークンとは､ 「これはジェイソンです」という完全な文字列があり､
それがスペースで区切られている場合です｡
あるトークンはthis､ 別のトークンはis､ 別のトークンはjasonかもしれません｡
文字列全体を取り出し､ デリミターに基づいて一定の境界で分割する方法です｡
そして､ デリミターが実際の文字列で有効なものでないことを確認したい｡
ですから､
ファイルから読み込むときにもデリミタを使いますが､ 同じような考え方です｡
文章を単語に分割することをトークナイジングといいます｡ このstrtokという関数を使うんだ｡
また､ いくつかの解析用ストリングについてお話したいと思います｡ これは､ 文字が小文字か大文字か､
英数字か数字かをチェックするもので､
大量にあるんです｡
これらは主に文字に対して行われますが､ 横文字の文字や､ 文字列全体に適用することも可能です｡
さて､ ここからは文字列についてもう少し詳しく説明します｡
そして､ これらの関数のいくつかを理解するために､ ポインタについて少し話をする必要があります｡
ポインターは､ C言語では非常に大きな概念である｡
ポインターについては､ 一冊まるごと紹介する予定です｡
おそらく､ 次回作か次々回作でしょう｡
そして､ いろいろな議論が交わされます｡ 言語の中で最も混乱しやすい話題でしょう｡
今は関係ないので詳しくは説明しませんが｡
ポインタとは何か､ これから使う関数を理解するために､ ちょっとだけ触れておきたいと思います｡
それだけが目的です｡ ですから､ この2､ 3のスライドを見た時点で､
ポインターを完全に理解していなくても､ 心配しないでください｡
そこで､ ポインターの概念ですが､ これまた後で詳しく説明することにします｡
しかし､ これらの文字列関数のいくつかを理解するために､ ちょっと覗いてみたいと思います｡
C言語では､ ポインタと呼ばれる非常に便利なタイプの変数が用意されています｡
という話をしたことがあります｡ それは､ メモリと､ メモリの割り当てに関係している｡
プログラミングの経験があり､ まったくの初心者でなければ､
自動的にメモリ管理をしてくれる言語もあります｡
メモリを確保して削除してくれる､ javaのような言語がこれにあたります｡
ガベージコレクタと呼ばれるものです｡ cのような言語ではこうはいかない｡
そのため､ ポインターを使用するためのメモリを作成すると､
必ずそのメモリを削除しなければなりません｡
しかし､ ポインターとは何かというと､ それはとても便利なもので､
アドレスを格納する変数のことです｡
アドレスとは､ メモリ上のものであることを思い出してください｡
通常は16進数です｡
アドレスの値は､ メモリ内の別の場所にあります｡
ポインターは､ そのアドレスが値を含むアドレスを指すだけです｡
また､ 過去にscanf関数で実際にアドレスを使用したことがあります｡
アンパサンドという記号は､ 演算子のアドレスを表していますね｡
Scanf関数の第2引数には､ データを格納したい変数を取り込む必要があり､
その変数はポインタでなければならない｡
それで､ scanf関数はポインターを期待したのです｡
単体で整数を作るには､ ポインタではありませんが､ そのアドレスを取得すると､
今度はポインタになるのです｡
つまり､ アンパサンドを使って､ プリミティブなデータ型のアドレスを取得しているわけです｡
ということは､ アドレスの概念には少し馴染みがあるんですね｡
ポインターの場合､ 今からint番号を作成すると､
それはポインターではなく､ 25に等しくなることを思い出してください｡
しかし､ それのアドレスであるアンパサンドを取得すれば､ それをポインタに代入することができるのです｡
ポインターはcでは特殊な構文で､ アスタリスクはポインターを表します｡
そこで､ 値25の変数numberを宣言し､ pNumberというアスタリスクを使ったポインタを宣言し､
numberのアドレスを格納しています｡
そしてまた､ アスタリスクはポインターを宣言するために使われます､ それはそれがポインターデータ型であることを知る方法です｡
p numberは何だから､ 変数p numberの値を得るには？
これはポインターで､ メモリ上のアドレスを指している｡
p数の値だけを見ようとすると､ アドレスが表示されるんです｡
アドレスが指しているものの値を取得したい場合は､ 参照を解除する必要があります｡
cではアスタリスクと変数名という特殊な構文を使って､
その値を含むメモリ位置に実際にアクセスすることができます｡
だから､ そのメモリー位置にデータを割り当てたい場合は､ スターpナンバーと言わなければならないのです｡
この場合も､ ポインターで指定されたアドレスに格納されているデータにアクセスする効果があります｡
ですから､ ポインタの話をすると､ この概念に慣れていないため､ 非常に混乱します｡
変数に値があると思うのは､ それだけです｡ しかし､ 変数はアドレスを持つことができ､
そのアドレスは値を持つことができます｡
このスライドは､ 今お話したことを説明しようとするものです｡
ある本にこんなことが書いてありました｡
メモリ上のポインタの概念と､ アドレスでどのように物事が動くかを示すスライドです｡
だから､ さっきの話の繰り返しになっちゃうんだけどね｡
しかし､ アンパサンドナンバーの値はナンバーの位置するアドレスです｡
つまり､ numberとp numberという2つの変数があって､
p numberがポインター､ numberがintだとすると､
ついでにポインターの型も指定しなければならない｡
ポインターはアドレスであっても､
その型やアドレスが指す場所を指定します｡
つまり､ int star p の数値は､ 数値のアドレスに等しくなるわけです｡
Numberはメモリ上にあり､ 例えば1000のアドレスを指していて､
その値は25です｡
Pナンバーは､ その実際のPナンバーのアドレスは105ですが､
そのアドレスの値は1000アドレスです｡
p個の数値の値を得るためには､ 1000をデリファレンスする必要があり､ そうすると25が得られる｡
なぜポインターの話をしたかというと､ 多くの文字列関数がポインターを返すからです｡
そんなに重要なことではなさそうです｡
配列の中のある文字がどのように取得されているかを理解したい場合にのみ､ 重要です｡
でも､ 簡単に触れておきたいと思ったんです｡
この概念は､ 後のセクションで詳細に説明しますので､ 今すぐには理解できなくても大丈夫です｡
しかし､ これから使う文字列関数はポインターを返すので､ そのポインターを利用して何か便利なことをするのだと理解してください｡
これまでstrコピーやstrコンペアを使っていたときは､ ポインターを渡しても､
そのポインターが返ってくることはなかったんです｡
さて､ ポインターを使わなければならないので､ その意味について少し説明したいと思います｡
ポインターは､ アドレスを指すようになるだけです｡
そして､ そのアドレスを使って､ パースや必要な処理を行うことができるのです｡
だから､ これで圧倒されなかったらいいんだけど｡ 繰り返しになりますが､ 完全に理解できなくても気にしないでください｡
これから､ 文字列の解析､ トークン化､
検索を行う例をいくつか見ていきましょう｡
文字列を検索するには､ strchrやstrstrを使うと述べました｡
strchrは､ 与えられた文字列から1文字を検索する｡
この関数の最初の引数は､ 検索対象の文字列で､
文字配列のアドレスになります｡
第2引数には､ 探しているキャラクターを指定します｡
その一例をご紹介します｡ そこで､ この関数は､ 文字列を先頭から検索し､
文字が見つかった文字列の最初の位置へのポインタを返そうとします｡
このようにポインタが重要なのは､ 値全体を返すのではなく､
文字列内の文字へのポインタを返すからで､ そのアドレスは本質的に配列のインデックスを表しています｡
つまり､ メモリ上のこの位置のアドレスは､ タイプcharacter starで､
charへのポインタと記述されていますが､
これは実際にはメモリ上の1文字に過ぎません｡
返された値を格納するためには､ 文字のアドレスを格納できる変数を作成する必要があるので､
ポインタを作成する必要があります｡
もしその文字が見つからなければ､ この関数は特別な値､ つまりnullを返します｡
Nullは､ ポインタが何も指していないことを意味します｡
ポインターの話をするときにnullを使いますが､ nullは何も見つからなかったというだけのことです｡
だから､ 何も指し示していないのです｡
何かを指し示すと､ その文字が見つかった場所のアドレスを指し示し､ そこからパースを始めることができます｡
というのも､ 以前の講義で思い出したのですが､
メモリ上に文字配列を作成する場合､ 各文字をバイトで表現しているだけなのです｡
また､ 文字列を解析しているときに､
ある文字がその文字列のどこから始まるかを知りたい場合は､ そのバイトを返せば､
そこから調べ始めることができます｡
で､ str char､ どう使うかというと､ こうです｡
文字列を作ると言うことですが､ ここではクイックブラウンフォックスで初期化します｡
次に､ 1つの文字を作成し､ それをqで初期化します｡
なぜなら､ str charから返されるポインタによって､ 文字列のどこに文字があるのかがわかるからです｡
ポインターをnullに初期化します｡ 今は何も指し示すことができないからです｡
次に､ str charを呼び出して､ クイックブラウンフォックスという文字列を渡し､
qという一文字を渡すことにします｡
これから起こることは､ 素早い茶色い狐の内側を見ることです｡
そして､ 最初にqを見つけると､ そのqのアドレスを返すことになる｡
つまり､ p got charは､ 実際にはstrの4番目のインデックスを指していることになります｡
tはサブゼロ､ hはサブ1､ eはサブ2､ spaceはサブ3､
qはサブ4だから｡
つまり､ p got charは､ quick brown foxのqを指すことになる｡
そうすれば､ その文字列のどこからその文字が始まるのかが正確にわかるようになります｡
つまり､ str charの第1引数は､ 最初に検索する場所のアドレスで､ これが検索文字列となる｡
第2引数は､ 求める文字､ 1文字です｡
第2引数はint型であることが期待される｡
つまり､ 厳密にはstr char､ 第2引数で取り込むのです｡
しかし､ cコンパイラは自動的にチャートをintに変換してしまいます｡
基本的にASCII値が取得されます｡
ですから､ 実際にはqを1文字ではなく､
intとして定義することができます｡
それはもっと専門的なことで､ あなたに知っておいてほしかったんです｡
しかし､ p got charはqから始まるので､ 値クイックブラウンフォックスを指し､
それをプリントアウトしようとするとヌル末尾に行くことになるのです｡
ですから､ この例を実行すると､ 実際には茶色い狐のように素早くなります｡
このように､ 文字列全体から一文字を検索することができます｡
str char を使用し､ 次にポインタを使用し､ そしてポインタを表示する｡
また､ そのポインターはどのように表示するのですか？ 他の文字を表示するのと同じように､ printf､ パーセントsを使用します｡
つまり､ これは文字列全体から文字を検索するための素敵な仕組みなのです｡
文字列の中にある文字列､ つまり部分文字列を検索したい場合は､ strstrを使用します｡
検索機能の中で最も便利な機能でしょう｡
1つの文字列から部分文字列の出現を検索します｡
そして､ 最初の文字列の中で部分文字列が見つかった位置へのポインタを返します｡ str
charと非常によく似ていますが､ 文字列全体を検索しています｡ 一致するものがない場合は､ nullを返す｡
第一引数には､ str charと同様に検索対象となる文字列を指定します｡
2つ目は､ 探している部分文字列､ 別の文字列です｡ そこで､ 一例をご紹介します｡
1文字を探すのではなく､ 単語や文字列を探すという点を除けば､
他のものと非常によく似ています｡
そこで､ dogと同じ意味のwordを用意しました｡
そして､ 「every dog has his day」というテキストがあります｡
ポインタをもう一度作成するのは､ このポインタが必要だからです｡
strstrを呼び出すとき､ 調べたい文字列をテキストで渡している｡
そして､ 第2引数には､ その文字列の中を調べたい単語を指定します｡
つまり､ wordに格納されている文字列のうち､ 最初に出現する文字列を検索することになる｡
文字列の犬は､ テキストの7文字目に表示されます｡
テキストを見れば
テキストサブ6がドッグと同等になる､ そこからがスタート｡
つまり､ ポインタはtextに6ドッグを加えたアドレスを指すことになり､
そこから始まって､ ヌルの終端まで行くことになります｡
だから､ 発見されたのは､ 犬には犬の日があるのです｡
また､ 大文字と小文字を区別して検索するので､ 注意が必要です｡
実際に大文字のD-O-Gを用意しても､ 何も見つからず､
正しい大文字を探すだけです｡
しかし､ これは部分文字列や文字列を見つけるのに使われます｡
プログラムで､ この文字列はこの文字列の中にあるのか､
と言うことがよくあるので､ とても便利です｡
ご理解いただけたでしょうか？ 文字列をトークン化する｡
トークンは､ 区切り文字で区切られた文字列の1つである｡
デリミタは何でも良いが､ その文字列に固有のものである必要がある｡
デリミタが「A」でないのは､ その文字列の中から有効な単語を見つけようとしているからです｡
スペースやカンマなど､ ユニークなものにする｡ これも､ 通常1つの文字列の中の単語の境界線として使われる｡
つまり､ 文章を単語に分解するわけです｡ これをトークナイジングといいます｡
このstrtok関数は､ 文字列をトークン化するために使用されます｡
2つの引数を必要とします｡
トークン化される文字列｡ これは､ スペースを持ち､ デリミタを持つ文字列全体である｡
そして､ 正の限定文字をすべて含む文字列｡
境界線として設定するものを指定できます｡
そこで､ 一例をご紹介します｡
文字配列80を作成しています｡
こんにちは､ お元気ですか､ あなたはダッシュです､ 私の名前はダッシュ・ジェイソンですと書いてあります｡
そして､ 区切り文字であるマイナス､ ダッシュを表す定数文字列を作成します｡
見た目は少し違いますが､
やはりマイナス､ ダッシュ､ どちらもダッシュなんです｡
そして､ トークンと呼ばれるポインタを作成します｡
なぜなら､ strtokは私たちの各トークンを返すつもりだからです｡
つまり､ strtokを呼び出してstringを渡し､
デリミタsを渡すと､ tokenは最初に見つけたものと同じになる｡
つまり､ 最初にstrtokを呼ぶと､ hello how are you spaceと等しくなり､
その後､ ダッシュとのマッチングを探します｡
そして､ 2回目の電話をかけると､ 私の名前がプリントアウトされるようになっているのです｡
3回目に呼び出すと､ ダッシュをスルーしてしまうので､
ジェイソンがプリントアウトされます｡
そのため､ ここではwhileループを使用しています｡ トークンがヌルと等しくない間は､ トークンをプリントアウトするように言っています｡
そして､ strtokを呼び出して次のトークンを取得する｡
そして､ 最初のパラメータをnull､
2番目のパラメータをsとし､
表示します｡ そこで､ より理解しやすくするために､
コードブロックで見てみましょう｡
では､ これを作ってみましょう｡ 今､ この例を見たところです｡
ここで何が起こっているのかを明確にするために､ 出力されたものをお見せしたいと思います｡
最初のトークンをプリントアウトしているのがわかります｡
まず､ 最初のトークンを見つけて､ それをプリントアウトして､
hello how are you, my name is, そして Jason とします｡
これで､ hello how are youとなり､ 最初のダッシュを見つけ､
それをプリントアウトすることができます｡ 私の名前は､ 次のトークンを見つけると､
3番目はジェイソンです｡ しかし､ それぞれのトークンを取得するために3回呼び出す必要があります｡
最初のものは､ str上でトークン化するものです｡
次のトークンを取得したいだけなので､ nullを渡すことができます｡
で､ 2つ目はnullを渡して､ sを渡すだけです｡
すでにtokされているstrの次のtokを出します｡
というわけで､ strtokの使い方はこんな感じです｡ パースする際にとても役立ちます｡
何かを解析して､ ある境界線を取り出すことができる｡
最後に紹介するのは､ これらの解析用文字列関数です｡
たくさんあるので､ ここにちょっとだけ表を用意しました｡
下であり､ 上である｡ 思ったとおりのことができる｡
これは､ 文字が小文字か大文字かをテストするものです｡
ある文字をテストしているのですが､ 文字列の中で使われているんです｡
そして､ これらの関数の引数は､ それぞれ探したい文字です｡
そして､ これらの関数はすべて､ 文字がテスト対象のセット内にあれば､
int型の0でない値を返します｡
そこで､ 内部の単語を探すために､ 内部にint文字を渡すことにします｡
単語の中に大文字があれば教えてくれますし､ 文字的に個々の要素の解析にも利用できます｡
つまり､ 戻り値はtrueかfalseに変換されるので､
ブール値としても使えるのがいいところです｡
So is lower は､ 単語や文字が小文字の場合､
真を返します｡
さて､ ここで私が実行したかったのは､
文字列全体の中の文字桁数をカウントする素敵な例です｡
しかし､ ここで注目すべきは､ バッファ､
入力バッファを作成し､ カウントするための3つの整数を作成していることです｡
何文字以下の文字列を入力するように指示し､
それを読み込んでいます｡
そして､ その文字列が特定の文字を含んでいるかどうかを調べます｡
そして､ 配列の各インデックスを見て､ その文字列を繰り返し処理することになります｡
では､ その例をご紹介しましょう｡
バッファは､ 入力を格納するための文字配列です｡
それをscanfを使って入力します｡
スペースを入れずに文字列を入力するんだ､ 1つの文字列なんだよ｡
バッファの各要素を調べて､ is alpha､ is digit､
is punct関数を呼び出して､ これらの文字が含まれているかどうかを確認します｡ そして､
trueまたはfalseを返すかどうかに基づいて増分し､ 文字列の中に文字､
数字､ 句読点がいくつあるかを表示します｡
これを実行しよう｡
大文字を並べたり､ 数字を入れたりして入力するんです｡
そうすると､ リターンを押したときに､ 全部カウントされるんです｡ つまり､ 私のステアリングには24文字､
4桁の数字､ そして1つの句読点であるコロンが含まれていたと言うわけです｡
これらの文字関数を利用した小粋なプログラムですが､
文字列を解析するのに便利です｡ なぜなら､ 文字列を解析しているのに､
1文字ずつ見ていくだけだからです｡
ありがとうございます｡