🖥️

【C言語超入門】 第23回 文字列操作

2024/12/25に公開

https://youtu.be/blIxGlMTgrY

四国めたん
\textcolor{pink}{四国めたん: }教師役ですわ

ずんだもん
\textcolor{lime}{ずんだもん: }生徒役なのだ

\footnotesize \textcolor{pink}{四国めたん:} 皆さん、こんにちは。四国めたんです

\footnotesize \textcolor{lime}{ずんだもん:} ずんだもんなのだ。こんにちはなのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回もC言語のお勉強をしていきましょう

\footnotesize \textcolor{lime}{ずんだもん:} レッツゴーなのだ

\footnotesize \textcolor{pink}{四国めたん:} 前回はメモリ領域に対する操作についてお話ししましたわ

\footnotesize \textcolor{lime}{ずんだもん:} おぼえているのだ

\footnotesize \textcolor{pink}{四国めたん:} 今回は char型 の配列である文字列の操作を簡単にする関数を幾つか見ていきますわ

\footnotesize \textcolor{lime}{ずんだもん:} メモリ操作で十分ではないのか?

\footnotesize \textcolor{pink}{四国めたん:} 実際にはメモリ操作とおおむね同じですが、 char型 の配列に限定されているので、少し便利になっていますわ

\footnotesize \textcolor{lime}{ずんだもん:} 使い勝手がよさそうなのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところで英語も日本語も操作できるのか?

\footnotesize \textcolor{pink}{四国めたん:} いいえ、今回は英語の文字列の操作のみですわ

\footnotesize \textcolor{lime}{ずんだもん:} 日本語についても教えてほしいのだ

\footnotesize \textcolor{pink}{四国めたん:} 日本語は、結構、難しいので、後の回で少し触れますわ

\footnotesize \textcolor{lime}{ずんだもん:} 待っているのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、基本的な文字列操作の関数は、メモリ操作と同じく"string.h"で宣言されているので最初にインクルードしますわ

\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ

strncpy

\footnotesize \textcolor{pink}{四国めたん:} まず最初はstrncpyですわ

\footnotesize \textcolor{lime}{ずんだもん:} strncpy

\footnotesize \textcolor{pink}{四国めたん:} はい、指定した文字列をコピーする関数ですわ

\footnotesize \textcolor{lime}{ずんだもん:} memcpyとの違いはなんなのだ?

\footnotesize \textcolor{pink}{四国めたん:} おおむね同じですが、NUL終端をコピーしたら、コピーを終了しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 形式は同じなのか?

\footnotesize \textcolor{pink}{四国めたん:} 形式としてはchar* strncpy(char* dst, const char* src, size_t num)となりますわ

\footnotesize \textcolor{lime}{ずんだもん:} memcpyではvoid*となっている部分がchar*となっているのだ

\footnotesize \textcolor{pink}{四国めたん:} 対象が文字列ですから

\footnotesize \textcolor{pink}{四国めたん:} ちなみに、古い形式でstrcpyと云う関数がありますがセキュリティーの関係で使わない方が望ましいですわ

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、Visual Studioでは同様の関数としてerrno_t strcpy_s(char* dst, rsize_t num, const char* src)が推奨されていますわね

\footnotesize \textcolor{lime}{ずんだもん:} 汎用性を考えたらstrncpyの方が使いやすいのかな?

\footnotesize \textcolor{pink}{四国めたん:} そうですわね

\footnotesize \textcolor{lime}{ずんだもん:} ところで詳細について教えてほしいのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

\footnotesize \textcolor{pink}{四国めたん:} まず、コピー元のポインタをsrcに、コピー先のポインタをdstにセットしますわ

\footnotesize \textcolor{lime}{ずんだもん:} memcpyと同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} そしてnumにはコピーする最大の文字数をセットしますわ

\footnotesize \textcolor{pink}{四国めたん:} 基本的にはdstにセットしたメモリ領域のサイズをセットしますわね

\footnotesize \textcolor{lime}{ずんだもん:} コピーする文字数ではないのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、コピーはNULまでと決まっていますので、メモリ領域を超えてコピーしないようにしているのですわ

\footnotesize \textcolor{pink}{四国めたん:} ちなみにコピー先のメモリ領域のサイズは、最低でも(コピー元の文字数 + 1)バイトである必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} (+ 1)?

\footnotesize \textcolor{pink}{四国めたん:} はい、終端のNULをセットするために追加で1バイトを必要としますわね

\footnotesize \textcolor{lime}{ずんだもん:} ところでmemsetと同じく、コピー元とコピー先のメモリ領域が重なっていてはいけないのか?

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{lime}{ずんだもん:} memmoveに相当する関数はないのか?

\footnotesize \textcolor{pink}{四国めたん:} ありませんわね

\footnotesize \textcolor{pink}{四国めたん:} メモリ領域が重なっている場合には、memmoveで代用して下さい

\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ

\footnotesize \textcolor{pink}{四国めたん:} 戻り値に関してはdstにセットした値がそのまま返りますわ

\footnotesize \textcolor{pink}{四国めたん:} ではサンプルコードを見ていきましょう

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define SIZE (15)

void main() {
  char c[] = "This is a pen.";  // 14文字+NUL終端
  char* p = (char*)malloc(SIZE);

  if (p != NULL) {
    strncpy(p, c, SIZE);
    printf("%s\n", p);
  }
  free(p);
  p = NULL;
}

strncpyの結果

\footnotesize \textcolor{lime}{ずんだもん:} 「This is a pen.」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、問題なく文字列がコピーされていますわね

\footnotesize \textcolor{lime}{ずんだもん:} 詳細を説明してほしいのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

\footnotesize \textcolor{lime}{ずんだもん:} まず、#define _CRT_SECURE_NO_WARNINGSは何をしているのだ?

\footnotesize \textcolor{pink}{四国めたん:} 実はVisual Studioではstrcpy_s関数を推奨しているため、strncpy関数はエラーの対象となっていますわ

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、ですので、エラーの対象から外すために#define _CRT_SECURE_NO_WARNINGSをファイルの最初に宣言しますわ

\footnotesize \textcolor{pink}{四国めたん:} とりあえず#define _CRT_SECURE_NO_WARNINGSをコメントアウトしてみましょう

//#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define SIZE (15)

void main() {
  char c[] = "This is a pen.";  // 14文字+NUL終端
  char* p = (char*)malloc(SIZE);

  if (p != NULL) {
    strncpy(p, c, SIZE);
    printf("%s\n", p);
  }
  free(p);
  p = NULL;
}

strncpyのエラー

\footnotesize \textcolor{lime}{ずんだもん:} 「C4996 strncpy : This function or variable may be unsafe. ...」というエラーが出ているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、ですので#define _CRT_SECURE_NO_WARNINGSを使うのですわ

\footnotesize \textcolor{lime}{ずんだもん:} #define _CRT_SECURE_NO_WARNINGSを宣言するとエラーにならないのか?

\footnotesize \textcolor{pink}{四国めたん:} おまじないだと思って、そのまま宣言してくださいね

\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ

\footnotesize \textcolor{pink}{四国めたん:} 次にコピー元の文字列cは文字列の最後を示す終端文字を含めて15文字の配列ですわ

\footnotesize \textcolor{lime}{ずんだもん:} 終端文字、NULのことか...

\footnotesize \textcolor{pink}{四国めたん:} コピー先はmallocでメモリ領域を確保しておきますわ

\footnotesize \textcolor{lime}{ずんだもん:} 15文字分なのだ

\footnotesize \textcolor{pink}{四国めたん:} 最後にコピーした文字列を表示していますわ

strcpy_s

Visual Studioでは、strncpyの代わりにstrcpy_sの使用を推奨しています

形式としてはerrno_t strcpy_s(char* dst, rsize_t num, const char* src)です

strncpyと比べて、引数の順番と、戻り値がエラー番号になっているところが異なります

また、numにはコピー先のメモリ領域のサイズをバイト単位で指定するようになっています

当然ですが、コピー先のメモリ領域のサイズは、コピー元のNUL終端を含めた文字列の文字数より大きい必要があります

なお errno_t型 は、エラーを表す整数の型です

0は正常であることを示します

その他の数値については、"errno.h"にエラーが定義されています

ただ、strcpy_sは標準の関数ではなく、Visual Stdioでのみ使える関数ですので、汎用性としてはstrncpyの方が勝っています

安全性についてはstrcpy_sの方が勝っていますので、状況に応じて選択しましょう

strlen

\footnotesize \textcolor{pink}{四国めたん:} 次はstrlenですわ

\footnotesize \textcolor{lime}{ずんだもん:} strlen

\footnotesize \textcolor{pink}{四国めたん:} はい、文字列の長さを取得する関数ですわ

\footnotesize \textcolor{lime}{ずんだもん:} メモリ操作には無かった関数なのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、形式としてはsize_t strlen(const char* str)となりますわ

\footnotesize \textcolor{lime}{ずんだもん:} 解説をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} まず、文字列の先頭のポインタをstrにセットしますわ

\footnotesize \textcolor{lime}{ずんだもん:} 戻り値は文字列の長さか?

\footnotesize \textcolor{pink}{四国めたん:} その通り、NULまでの文字数を返しますわ

\footnotesize \textcolor{lime}{ずんだもん:} とりあえず、具体例をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define SIZE (20)

void main() {
  char c[] = "This is a pen.";  // 14文字+NUL終端
  char* p = (char*)malloc(SIZE);

  if (p != NULL) {
    size_t len = 0;
    strncpy(p, c, SIZE);
    len = strlen(p);
    printf("%sの長さは%llu\n", p, len);
  }
  free(p);
  p = NULL;
}

strlenの結果

\footnotesize \textcolor{lime}{ずんだもん:} 「This is a pen.の長さは14」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、なおstrlenの戻り値はあくまでも文字列の長さですわ

\footnotesize \textcolor{lime}{ずんだもん:} メモリ領域のサイズ、20バイトとは関係がないのか

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{pink}{四国めたん:} さらにsizeofと異なり配列でもmallocで取得したメモリ領域でも文字列でさえあれば正しい値を取得できますわ

\footnotesize \textcolor{lime}{ずんだもん:} 使い勝手がいいのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみにstrlenの戻り値が size_t型 ですので変数lensize_t型 で宣言していますわ

\footnotesize \textcolor{lime}{ずんだもん:} ところでprintf中の%lluはなんなのだ?

\footnotesize \textcolor{pink}{四国めたん:} size_t型 の値をprintfで出力する場合には、書式指定として"%llu"を使用しますわ

\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ

strnlen / strnlen_s

Visual Studioでは、strlenの代わりにstrnlenstrnlen_sの使用を推奨しています

形式としてはsize_t strnlen(const char* str, size_t num)size_t strnlen_s(const char* str, size_t num)です

strlenと比べて、引数にstrのメモリ領域のサイズnumが追加されているところが異なります

numには文字列のメモリ領域のサイズをバイト単位で指定するようになっています

文字列にNUL終端が存在しない場合に、numバイト以上のオーバーランを起こさないようになっています

戻り値は文字列の文字数です

numにセットした値が返った場合には、エラーです

strnlenstrnlen_sも標準の関数ではなく、Visual Stdioでのみ使える関数ですので、汎用性としてはstrlenの方が勝っています

安全性についてはstrnlenstrnlen_sの方が勝っていますので、状況に応じて選択しましょう

strncat

\footnotesize \textcolor{pink}{四国めたん:} 次はstrncatですわ

\footnotesize \textcolor{lime}{ずんだもん:} strncat

\footnotesize \textcolor{pink}{四国めたん:} はい、文字列の後ろに、指定した文字列を連結する関数ですわ

\footnotesize \textcolor{lime}{ずんだもん:} 文字列の連結はよく使うのか?

\footnotesize \textcolor{pink}{四国めたん:} 意外に使いますわね

\footnotesize \textcolor{lime}{ずんだもん:} そうなのか...

\footnotesize \textcolor{pink}{四国めたん:} 形式としてはchar* strncat(char* dst, const char* src, size_t num)となりますわ

\footnotesize \textcolor{lime}{ずんだもん:} strncpyと似ているのだ

\footnotesize \textcolor{pink}{四国めたん:} strncpyでは上書きしますが、strncatでは後ろに付け加えるのが特徴ですわ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに、古い形式でstrcatと云う関数がありますが、セキュリティーの関係で使わない方が望ましいですわ

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、Visual Studioでは同様の関数としてerrno_t strcat_s(char* dst, size_t num, const char* src)が推奨されていますわね

\footnotesize \textcolor{lime}{ずんだもん:} これもstrncpyと同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} そうですわね

\footnotesize \textcolor{lime}{ずんだもん:} ところで詳細について教えてほしいのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

\footnotesize \textcolor{pink}{四国めたん:} まず、連結元の文字列のポインタをsrcに、連結先の文字列のポインタをdstにセットしますわ

\footnotesize \textcolor{lime}{ずんだもん:} strncpyと同じなのだ

\footnotesize \textcolor{pink}{四国めたん:} そして連結先にコピーする文字列の最大の文字数をnumにセットしますわ

\footnotesize \textcolor{pink}{四国めたん:} 通常は連結先のメモリ領域を超えて文字がコピーされないような文字数としますわね

\footnotesize \textcolor{lime}{ずんだもん:} (連結先のメモリ領域のサイズ - 連結先の文字数)でいいのか?

\footnotesize \textcolor{pink}{四国めたん:} そうですわね

\footnotesize \textcolor{pink}{四国めたん:} ただ、終端のNULを考慮すれば、連結先のメモリ領域のサイズは、(連結先の文字数 + 連結元の文字数 + 1)バイト以上であることが必要ですわ

\footnotesize \textcolor{lime}{ずんだもん:} りょうかいなのだ

\footnotesize \textcolor{pink}{四国めたん:} なお、戻り値に関してはdstにセットした値がそのまま返りますわ

\footnotesize \textcolor{pink}{四国めたん:} サンプルコードを見てみましょう

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>

#define SIZE (50)

void main() {
  char c1[SIZE] = "This is a pen.";  // 14文字+NUL終端
  char c2[] = "This is an apple.";   // 17文字+NUL終端
  size_t len = SIZE - strlen(c1);

  strncat(c1, c2, len);
  printf("%s\n", c1);
}

strncatの結果

\footnotesize \textcolor{lime}{ずんだもん:} 「This is a pen.This is an apple.」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、問題なく文字列が連結されていますわね

\footnotesize \textcolor{lime}{ずんだもん:} 詳細の説明をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、まずc1を文字列として宣言、初期化していますわ

\footnotesize \textcolor{lime}{ずんだもん:} 配列のサイズをSIZEとして指定する必要があるのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、文字列を連結した後に必要なサイズの配列を確保する必要がありますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} 次にc2を文字列として宣言、初期化していますわ

\footnotesize \textcolor{lime}{ずんだもん:} c2は配列のサイズを指定しなくてもいいのか?

\footnotesize \textcolor{pink}{四国めたん:} 連結元の文字列は文字数分のサイズが確保されていればOKなので、サイズの指定は、いりませんわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} 引数numにセットする長さlenは、連結先のメモリサイズSIZEからc1の文字数をstrlenで取得して引いていますわ

\footnotesize \textcolor{lime}{ずんだもん:} c2の文字列のサイズ、(17文字 + 1)バイトには十分なサイズなのだ

\footnotesize \textcolor{pink}{四国めたん:} あとはstrncatで文字列を連結した後、printfで表示していますわ

strcat_s

Visual Studioでは、strncatの代わりにstrcat_sの使用を推奨しています

形式としてはerrno_t strcat_s(char* dst, size_t num, const char* src)です

strncatと比べて、引数の順番と、戻り値がエラー番号になっているところが異なります

また、numには連結先のメモリ領域のサイズをバイト単位で指定するようになっています

strncatと異なり、(連結先のメモリ領域のサイズ - 連結先の文字数)ではないことに注意が必要です

余計な計算をする必要がない分、strncatよりも使い勝手は良いかもしれません

当然ですが、連結先のメモリ領域のサイズは、NUL終端を含めた、連結元の文字列と連結先の文字列の合計よりも大きい必要があります

なお errno_t型 は、エラーを表す整数の型です

0は正常であることを示します

その他の数値については、"errno.h"にエラーが定義されています

ただ、strcat_sは標準の関数ではなく、Visual Stdioでのみ使える関数ですので、汎用性としてはstrncatの方が勝っています

安全性についてはstrcat_sの方が勝っていますので、状況に応じて選択しましょう

strcmp

\footnotesize \textcolor{pink}{四国めたん:} 次はstrcmpですわ

\footnotesize \textcolor{lime}{ずんだもん:} strcmp

\footnotesize \textcolor{pink}{四国めたん:} はい、指定した文字列を比較して同じかどうかをチェックする関数ですわ

\footnotesize \textcolor{lime}{ずんだもん:} memcmpと同じ感じか?

\footnotesize \textcolor{pink}{四国めたん:} はい、形式としてはint strcmp(const char* str1, const char* str2)となりますわ

\footnotesize \textcolor{lime}{ずんだもん:} 解説をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} まず、比較する文字列へのポインタをstr1str2にセットしますわ

\footnotesize \textcolor{lime}{ずんだもん:} 戻り値は比較の結果か?

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{pink}{四国めたん:} 完全に一致していれば0が、一致していなければ0以外が返りますわ

\footnotesize \textcolor{lime}{ずんだもん:} とりあえず、具体例をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

#include <stdio.h>
#include <string.h>

void main() {
  char c1[] = "This is a pen.";  // 14文字+NUL終端
  char c2[] = "This is an apple.";   // 17文字+NUL終端

  if (strcmp(c1, c2)) {
    printf("c1とc2は異なる文字列です\n");
  } else {
    printf("c1とc2は同じ文字列です\n");
  }
}

strcmpの結果

\footnotesize \textcolor{lime}{ずんだもん:} 「c1とc2は異なる文字列です」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、strcmpは文字列が一致していないので0以外を返しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 0以外の整数ということは、真偽値ではtrueと同じということか

\footnotesize \textcolor{pink}{四国めたん:} はい、ですのでprintfでは「c1とc2は異なる文字列です」が表示されますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに比較する文字列を変えて同じ内容にすれば、結果は「c1とc2は同じ文字列です」と表示されますわ

\footnotesize \textcolor{lime}{ずんだもん:} ところで#define _CRT_SECURE_NO_WARNINGSは無くてもいいのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、strcmpはセキュリティー上の問題はありませんので不必要ですわね

strstr

\footnotesize \textcolor{pink}{四国めたん:} 次はstrstrですわ

\footnotesize \textcolor{lime}{ずんだもん:} strstr

\footnotesize \textcolor{pink}{四国めたん:} はい、文字列から指定した文字列を検索しますわ

\footnotesize \textcolor{lime}{ずんだもん:} メモリ操作には無かった関数なのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、形式としてはchar* strstr(const char* str1, const char* str2)となりますわ

\footnotesize \textcolor{lime}{ずんだもん:} 解説をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} まず、検索対象の文字列へのポインタをstr1にセットしますわ

\footnotesize \textcolor{pink}{四国めたん:} そして検索する文字列へのポインタをstr2にセットしますわ

\footnotesize \textcolor{lime}{ずんだもん:} 戻り値は char型 のポインタなのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、検索対象の文字列中に検索文字列が見つかった場合は、最初にヒットした文字列の先頭のポインタが返りますわ

\footnotesize \textcolor{lime}{ずんだもん:} 検索文字列が見つからなかった場合にはどうなるのだ?

\footnotesize \textcolor{pink}{四国めたん:} 検索文字列が見つからなかった場合にはNULLが返りますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} サンプルコードを見てみましょう

#include <stdio.h>
#include <string.h>

void main() {
  char c1[] = "This is a pen.";  // 14文字+NUL終端
  char c2[] = "pen";
  char* p1 = strstr(c1, c2);
  char* p2 = strstr(c1, "a");

  if (p1 != NULL) {
    printf("pen以下の文字列は\"%s\"です\n", p1);
  }
  if (p2 != NULL) {
    printf("a以下の文字列は\"%s\"です\n", p2);
  }
}

strstrの結果

\footnotesize \textcolor{lime}{ずんだもん:} 「pen以下の文字列は"pen."です」、「a以下の文字列は"a pen."です」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、どちらもちゃんと検索できましたわね

\footnotesize \textcolor{lime}{ずんだもん:} 解説をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、p1には文字列c1からc2を検索していますわ

\footnotesize \textcolor{lime}{ずんだもん:} c1にはc2の"pen"が含まれているので、p1には"p"へのポインタがセットされるのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、そしてp2は1文字の文字列"a"をc1から検索していますわ

\footnotesize \textcolor{lime}{ずんだもん:} c1には"a"が含まれているので、p2には"a"へのポインタがセットされるのだ

\footnotesize \textcolor{pink}{四国めたん:} そして得られたポインタからNUL終端までの文字列をprintfで表示していますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところでstrstr(c1, 'a')と、検索文字列に"a"の代わりに、文字'a'を使ってはいけないのか?

\footnotesize \textcolor{pink}{四国めたん:} 'a'は char型 で、ポインタ型ではありませんので、使えませんわね

\footnotesize \textcolor{pink}{四国めたん:} 無理に文字 a を検索文字列として指定すると警告が出ますし、実行するとエラーとなりますわね

\footnotesize \textcolor{lime}{ずんだもん:} 検索文字列に文字を使うのは止めておくのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに文字を検索する場合にはchar* strchr(const char* str, int character)という別の関数があるので、そちらを使いますわ

\footnotesize \textcolor{lime}{ずんだもん:} 指定する文字は int型 だが問題ないのか?

\footnotesize \textcolor{pink}{四国めたん:} まぁ、 char型int型 に代入できるので、OKですわ

\footnotesize \textcolor{lime}{ずんだもん:} わかったのだ

\footnotesize \textcolor{lime}{ずんだもん:} ところでstrstrでも#define _CRT_SECURE_NO_WARNINGSは無くてもいいのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、strstrは多少セキュリティー上の問題はありますが、不必要ですわね

memchr

メモリ操作には、strchrと同じ動作をするmemchrという関数があります

形式はvoid* memchr(const void* p, int value, size_t num)となります

なおpはチェックするメモリ領域へのポインタ、numはチェックするバイト数です

基本的にはメモリ領域のサイズを指定します

valueには検索する値をセットします

int型 ですが、検索する際には unsigned char型 として扱われます

検索はバイト単位で行われ、最初に見つかった値のアドレスが返ります

見つからなければNULLが返ります

strtok

\footnotesize \textcolor{pink}{四国めたん:} 最後はstrtokですわ

\footnotesize \textcolor{lime}{ずんだもん:} strtok

\footnotesize \textcolor{pink}{四国めたん:} はい、この関数は少し変わった関数で、文字列を指定の区切り文字で分割しますわ

\footnotesize \textcolor{lime}{ずんだもん:} 変わった関数なのか?

\footnotesize \textcolor{pink}{四国めたん:} 通常の関数は1回だけ呼び出して終わりですが、この関数は複数回、連続して呼び出すことで完了する、珍しい関数ですわ

\footnotesize \textcolor{lime}{ずんだもん:} 確かに珍しいのだ

\footnotesize \textcolor{pink}{四国めたん:} 形式としてはchar* strtok(char* str, const char* delimiters)となりますわ

\footnotesize \textcolor{lime}{ずんだもん:} ふむふむ

\footnotesize \textcolor{pink}{四国めたん:} なお、Visual Studioでは同様の関数としてchar* strtok_s(char* str, const char* delimiters, char** context)が推奨されていますわ

\footnotesize \textcolor{lime}{ずんだもん:} とりあえず詳細について教えてほしいのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、最初の呼び出しでは、分割する文字列へのポインタをstrにセットしますわ

\footnotesize \textcolor{lime}{ずんだもん:} 最初の呼び出し?

\footnotesize \textcolor{pink}{四国めたん:} はい、strtokは複数回、呼び出すことが前提ですので、最初の呼び出しと2回目以降の呼び出しは少し違いますわね

\footnotesize \textcolor{lime}{ずんだもん:} 2回目以降はどうするのだ?

\footnotesize \textcolor{pink}{四国めたん:} 2回目以降の呼び出しではstrNULLをセットしますわ

\footnotesize \textcolor{lime}{ずんだもん:} 分割する文字列をセットするのは初回だけなのだな

\footnotesize \textcolor{pink}{四国めたん:} 次にdelimitersには分割に使用する区切り文字を文字列としてセットしますわ

\footnotesize \textcolor{lime}{ずんだもん:} うん?区切り文字は1文字ではないのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、区切り文字は1文字ですが、複数の区切り文字を指定できますわ

\footnotesize \textcolor{lime}{ずんだもん:} それは便利なのだ

\footnotesize \textcolor{pink}{四国めたん:} そして戻り値には分割された文字列へのポインタが返りますわ

\footnotesize \textcolor{lime}{ずんだもん:} 分割した文字列の全てが返るのか?

\footnotesize \textcolor{pink}{四国めたん:} 基本的に、分割された文字列は複数になりますが、戻り値は1つの文字列しか返せませんわね

\footnotesize \textcolor{pink}{四国めたん:} ですので、初回の呼び出しの戻り値には、分割された最初の文字列が返りますわ

\footnotesize \textcolor{lime}{ずんだもん:} 2番目以降の分割された文字列は、どのように取得するのだ?

\footnotesize \textcolor{pink}{四国めたん:} 2番目以降の分割された文字列は、続けてstrtok関数を呼び出すことで取得しますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほど、それが 複数回、呼び出すことが前提 の意味なのか

\footnotesize \textcolor{pink}{四国めたん:} その通りですわ

\footnotesize \textcolor{pink}{四国めたん:} 2番目以降の分割された文字列は、続けてstrtok関数のstrNULLをセットして呼び出すことで得られますわ

\footnotesize \textcolor{lime}{ずんだもん:} 分割された文字列が無くなった場合には、どうなるのだ?

\footnotesize \textcolor{pink}{四国めたん:} 分割された文字列を取得できない場合にはNULLが戻り値として返りますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} ではサンプルコードを見ていきましょう

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>

void main() {
  char c[] = "This is a pen. This is an apple.";
  char* p = strtok(c, " .");

  while (p != NULL) {
    printf("%s\n", p);
    p = strtok(NULL, " .");
  }
}

strtokの結果

\footnotesize \textcolor{lime}{ずんだもん:} 「This」「is」「a」「pen」「This」「is」「an」「apple」と表示されたのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、しっかりと単語毎に文字列の取得ができていますわね

\footnotesize \textcolor{lime}{ずんだもん:} プログラムの解説をおねがいするのだ

\footnotesize \textcolor{pink}{四国めたん:} わかりましたわ

\footnotesize \textcolor{pink}{四国めたん:} 最初のstrtok関数のコールでは、分割する文字列cと区切り文字として空白' 'とピリオド'.'を文字列にまとめて与えていますわ

\footnotesize \textcolor{lime}{ずんだもん:} 区切り文字は幾つでも指定できるのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、全ての区切り文字を文字列にまとめて指定しますわ

\footnotesize \textcolor{pink}{四国めたん:} その後はNULLが返るまで繰り返しstrtok(NULL, " .")を呼び出していますわ

\footnotesize \textcolor{lime}{ずんだもん:} pには文字列へのアドレスが入るのか?

\footnotesize \textcolor{pink}{四国めたん:} はい、strtokを呼び出す毎に区切り文字で区切られた文字列のアドレスが入りますわ

\footnotesize \textcolor{lime}{ずんだもん:} なるほどなのだ

\footnotesize \textcolor{pink}{四国めたん:} ちなみに char型 の配列としてのcstrtok呼び出し後には変更されてしまいますわね

\footnotesize \textcolor{pink}{四国めたん:} ですので、cにはconstキーワードを付けてはいけませんわ

\footnotesize \textcolor{lime}{ずんだもん:} strtok関数が勝手に変更してしまうのか...

\footnotesize \textcolor{pink}{四国めたん:} まぁ、そういう意味でも珍しい関数ですわね

\footnotesize \textcolor{lime}{ずんだもん:} ところで#define _CRT_SECURE_NO_WARNINGSをファイルの最初に宣言しているのだ

\footnotesize \textcolor{pink}{四国めたん:} はい、strtok関数は、Visual Studioではエラーの対象なので必要ですわね

strtok_s

Visual Studioでは、strtokの代わりにstrtok_sの使用を推奨しています

形式としてはchar* strtok_s(char* str, const char* delimiters, char** context)です

strtokと比べてcontextが追加されています

なお、contextの型であるchar**は、 char型 のポインタのポインタです

char*のポインタですね

contextは関数内部でのみ使用します

とりあえず char*型 の変数を宣言して、そのアドレスをセットします

具体的には以下のように使用します

#include <stdio.h>
#include <string.h>

void main() {
  char c[] = "This is a pen. This is an apple.";
  char* pcontext = NULL;
  char* p = strtok_s(c, " .", &pcontext);

  while (p != NULL) {
    printf("%s\n", p);
    p = strtok_s(NULL, " .", &pcontext);
  }
}

strtok_sは標準の関数ではなく、Visual Stdioでのみ使える関数ですので、汎用性としてはstrtokの方が勝っています

安全性についてはstrtok_sの方が勝っていますので、状況に応じて選択しましょう

まとめ

\footnotesize \textcolor{pink}{四国めたん:} お疲れさまでした

\footnotesize \textcolor{lime}{ずんだもん:} おつかれさまなのだ

\footnotesize \textcolor{pink}{四国めたん:} 以上で 文字列操作 についての説明は終わりですわ

\footnotesize \textcolor{pink}{四国めたん:} 文字列操作 のための標準関数は他にもいろいろとあるので標準Cライブラリのページなどで確認して下さい。

Discussion