文字列の長さの取得
文字列操作1
C言語では、文字列はchar型の配列に代入して扱うことができます。
(→文字型と文字列)
(ポインタで扱うという方法もありますが、これはまた別の項で説明します。)
配列で扱う都合上、簡単な処理であってもやや複雑なコードを書かなければなりません。
文字列処理のために毎回コードを書いていては面倒なので、誰もがよく行うであろう処理はC言語の標準ライブラリ関数として提供されています。
自分で処理を書くよりも簡単で、バグの心配もないので特別な理由がなければ標準関数を使いましょう。
ヘッダファイルのインクルード
文字列操作関数を使用する前に、コードの先頭に#include <string.h>
という一行を追加する必要があります。
#include <stdio.h>
#include <string.h>
//↑を追加
#include
はインクルードと言い、「指定したファイルをその場所に展開する」という意味になります。
「○○.h」はヘッダファイルというもので、string.h
はC言語標準ライブラリ関数のうち、文字列操作関数の宣言が記述されたファイルです。
これを<
記号と>
記号で囲って指定します。
コード先頭でヘッダファイルをインクルードすることで、その場所にヘッダファイル内の内容がコード上に展開されるので、それ以降の行でヘッダファイル内にある関数等を使用することができるようになります。
ちなみにいままでインクルードしていたstdio.h
というヘッダファイルにはprintf
関数やgetchar
関数などの、標準入出力関数(standard input/output)の宣言が記述されています。
strlen関数
文字列の長さを取得するにはstrlen
関数を使用します。
#include <stdio.h>
#include <string.h>
int main()
{
char str[100] = "ABCDE";
printf("%d\n", strlen(str));
printf("%d\n", sizeof(str)); //単純な配列のサイズ
getchar();
}
5 100
- size_t strlen(
const char *str
); - 文字列strの先頭からNULL文字が出現するまでのバイト数を取得する。
引数には長さを取得したい文字列配列を指定します。
strlen
関数の戻り値は終端のNULL文字(\0
)までの文字数です。
char型は1バイトの情報量なので、つまりバイト数と同じです。
ただしNULL文字は除外された値です。
上のコードの配列の中身は「ABCDE」とNULL文字で、文字列全体で必要なバイト数は「6」ですが、strlen
関数の実行結果は「5」となります。
ちなみにsizeof
演算子はNULL文字などは考慮せずに単純に配列のサイズを返します。
const
strlen
関数の引数のstrにはconst
が付けられています。
これは引数に指定した値が関数内で書き換えが起こらないことが保証されています。
(→定数とマクロの項を参照)
ちなみに引数名の手前の「*」はポインタというもので、引数にポインタまたは配列を指定可能なことを示しています。
size_t型
戻り値のsize_t型は、以前はint型として使用できると説明しましたが、実際にはunsigned int型の別名です。
つまり戻り値がマイナスになることはありません。
そのままunsigned int型を返すように作られていても問題はないのですが、何らかのサイズを示す用途の型にsize_tという名前を付けることで、意図が明確になりコードが分かりやすくなるというメリットがあります。
なお、size_t型(unsigned int型)をint型変数で受け取ると暗黙の型変換が起こりますから、データが変化する場合があります。
この関数に限らず、戻り値を変数で受ける場合はできる限りその関数が返す通りのデータ型で受けるようにした方が良いです。
size_t型がunsigned int型というのも実は正確ではなく、より正確に言えばsize_t型の定義はC言語では決められておらず処理系定義です。
つまりコンパイラによって異なります。
ほとんどのコンパイラでは符号なし整数型というのは共通しているようです。
また、32bit環境と64bit環境でも異なり、64bit環境ではメモリやストレージ(ハードディスク等)に巨大なサイズが使用できるようになっているため、size_t型もそれらのサイズを表せるように64bit(以上)の長さで定義されているようです。
Windows版Visual Studioでは32ビット版は「unsigned int型(4バイト)」、64ビット版は「unsigned __int64型(8バイト)」と定義されています。
「__int64」は名前の通り64bit整数型で、long long型と同サイズです。
ファイルサイズなどを扱う場合にint型やunsigned int型を使用していると、別の環境に移植する時に不具合が起こる可能性があります。
コードの書き換えの手間を省くためにもsize_t型の使用が推奨されます。
名前が似ている関数たち
C言語の標準ライブラリには、同じような機能を提供する、同じような名前の関数が複数存在することがあります。
文字数を数える関数はstrlen
関数の他に、strnlen
、strnlen_s
等が存在します。
さらに、wcslen
、_mbsnlen
などもあります。
(本当はもっとあります)
「str」というのは文字列を意味する「string」の省略形です。
文字列操作関数の名前には大抵「str」が付けられています。
str「n」系
strnlen
関数はより安全なstrlen関数です。
引数がひとつ増えており、読み取る最大バイト数を指定できるようになっています。
(nはおそらくnumberかnumericの意味)
- size_t strnlen(
const char *str
size_t numberOfElements
); - 文字列strの先頭からNULL文字が出現するまでのバイト数を取得する。
ただしnumberOfElementsバイト以上のデータは読み取らない。
strlen
関数はNULL文字が出現するまでメモリ上のデータを読み込みます。
もしstrlen
関数にNULL文字で終わらないchar配列を指定すると、配列のメモリ領域を超えてデータを読み込み続けてしまいます。
これを防ぐために、strnlen
関数では第二引数で指定した文字数以上は読み取らないように最大値を指定できるようになっています。
第二引数以上の文字数を持つchar型配列を第一引数に指定すると、第二引数の値が返ります。
最初のサンプルコードのように、NULL文字で終わることが確実な場合はstrlen
関数でかまいませんが、できるだけ安全な関数を使用するクセを付けたほうが良いでしょう。
#include <stdio.h>
#include <string.h>
int main()
{
char str[8] = "ABCDE";
//不正な文字列操作が起こったと仮定
//終端のNULL文字が消去されている
str[5] = 'F';
str[6] = 'G';
str[7] = 'H';
//配列サイズの値を指定することで
//配列終端を超えて読み取ることを防ぐ
size_t size = strnlen(str, 8);
//sizeofで配列サイズを取得可能ならこちらでもOK
//size_t size = strnlen(str, sizeof(str));
printf("%u", size); //「8」を表示
getchar();
}
上記コードは不正な文字列操作によって終端のNULL文字が存在しない配列になってしまっています。
このような配列はC言語では文字列としては使用できません。
strnlen
関数は、このような危険な可能性のある配列でも、あらかじめ配列サイズが分かっているならば安全に読み取ることができます。
NULL文字の存在する正常な文字列配列の場合、文字列の長さは最大でも「配列サイズ - 1」です。
(NULL文字は含まないため)
上記コードは配列サイズと同じ「8」が返されており、不正な文字列が指定されたことがわかります。
なお、printf
関数の変換指定子には%u
を指定しています。
これはunsigned int型を表示する変換指定子です。
語尾に「_s」系
strnlen_s
関数はセキュリティの強化版です。
(語尾の「_s」はセキュリティ、セキュアのsです)
- size_t strnlen_s(
const char *str
size_t numberOfElements
); - 文字列strの先頭からNULL文字が出現するまでのバイト数を取得する。
ただしnumberOfElementsバイト以上のデータは読み取らない。
使い方はstrnlen
関数と同じです。
strnlen
関数は第一引数に不正な値(NULL)を指定するとエラーが発生し、プログラムが停止してしまいます。
strnlen_s
関数の場合は0を返し、プログラムは停止しません。
頭に「_mb」、「w」系
これらは日本語などの1バイトでは表せない文字(マルチバイト文字、ワイド文字)を扱うための関数です。
C言語では日本語ひとつを表すのに2バイト以上を必要とします。
つまり「あ」という一文字を扱う場合でもchar型の要素数2以上の配列が必要ということです。
(プラスNULL文字の分の容量も必要です)
これをマルチバイト文字といいます。
strlen
関数は日本語などの文字は想定しておらず、正確な文字数を得ることができません。
日本語を含む文字列の文字数を正確にカウントするには_mbstrlen
関数を使用します。
この関数の使用にはコード先頭に#include <stdlib.h>
と#include <locale.h>
の追加が必要です。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>
int main()
{
char str[100] = "あいうえおABC";
printf("%u\n", strlen(str));
setlocale(LC_ALL, "");
printf("%u", _mbstrlen(str));
getchar();
}
13 8
- size_t _mbstrlen(
const char *str
); - 文字列の長さを取得する(マルチバイト版)
10行目のstrlen
関数では日本語を2文字とカウントしてしまい、正しい文字数は取得できません。
14行目の_mbstrlen
関数では日本語も1文字とカウントし、正しい文字数(8文字)を取得できます。
_mbstrlen
関数で正しい文字数を取得するには、その前にsetlocale
関数を実行する必要があります。
この関数は、マルチバイト系の関数で使用する「地域、言語」の設定(ロケール)を行います。
詳細な使い方は省略しますが、マルチバイト文字系の関数を使用する前にサンプルコードのように一度だけ呼び出しておく、と覚えておけば問題ありません。
実は_mbstrlen
関数はVisualStudioでしか使用できない、マイクロソフトの独自の関数です。
他のコンパイラでは使用できません。
また、上記コードのstrlen
関数の実行結果は「13」となっていますが、これもコンパイラによって異なります。