C言語での文字列切り出し完全ガイド|標準関数・自作関数・マルチバイト対応

目次

1. はじめに

C言語における文字列の操作は、プログラミングを学ぶうえで重要なスキルの一つです。特に、文字列の切り出し(部分文字列の抽出)は、データの処理やフォーマット変換を行う際によく使われます。

本記事では、C言語で文字列を切り出す方法について、標準ライブラリ関数を使用する方法、自作関数を作成する方法、マルチバイト文字(日本語)への対応、文字列の分割方法などを詳しく解説します。また、応用例やエラー処理についても紹介するので、ぜひ最後までご覧ください。

この記事で学べること

本記事を読むことで、次のようなスキルを習得できます。

  • C言語の文字列の基本概念と終端文字 ' ' の役割
  • strncpystrchr などの標準ライブラリ関数を使った部分文字列の切り出し
  • 自作関数を用いた文字列操作の実装方法
  • マルチバイト文字(日本語)を考慮したUTF-8 や Shift_JIS 文字列の処理
  • strtok を使った文字列の分割方法
  • 特定の文字の前後を取得する方法とその応用例

初心者の方でも理解しやすいように、コード例を交えながら解説していきます。

なぜC言語で文字列の切り出しが重要なのか?

C言語は文字列を「配列(char型の配列)」として扱うため、他の高級言語(PythonやJavaScriptなど)のように簡単に部分文字列を取得することができません。そのため、以下のような場面で適切な方法を選択することが重要になります。

1. 入力データの処理

たとえば、ログデータやCSVファイルなどのデータを解析する際に、特定の項目を抽出する必要があります。

2. 特定のキーワードを検索

ある文字列の中から特定のキーワードを探し、その前後の情報を取得することは、検索機能やデータ抽出に不可欠です。

3. プログラムの安全性向上

strncpy のような関数を適切に使用することで、バッファオーバーフロー(バッファサイズを超えるデータの書き込み)を防ぐことができます。これは、セキュリティ上のリスクを回避するために重要です。

本記事の構成

本記事では、以下の流れで解説を進めます。

  1. C言語の文字列とは?基本概念と終端文字の重要性
  2. C言語で部分文字列を切り出す方法【標準ライブラリ編】
  3. C言語で部分文字列を切り出す方法【自作関数編】
  4. 文字コードごとの文字列切り出し方法
  5. C言語で文字列を分割する方法
  6. 応用例: 特定の文字の前後を抽出する方法
  7. まとめ
  8. FAQ

それでは、まずは「C言語の文字列とは?基本概念と終端文字の重要性」について詳しく見ていきましょう。

2. C言語の文字列とは?基本概念と終端文字の重要性

2.1 C言語の文字列の基本概念

文字列は「charの配列」

C言語では、文字列を文字の配列(char型の配列)として扱います。例えば、以下のコードは文字列の定義と表示の基本的な例です。

#include <stdio.h>

int main() {
    char str[] = "Hello, World!"; // 文字列リテラルを配列として定義
    printf("%s
", str); // 文字列を出力
    return 0;
}

このコードでは、"Hello, World!" という文字列が char 型の配列として格納され、printf("%s", str); によって出力されます。

文字列の内部構造

文字列 "Hello" は、メモリ上では次のように格納されます。

インデックス012345
文字Hello

C言語では、文字列の終端を示す特別な文字(ヌル文字 ' ')が最後に自動的に追加されるため、文字列の長さは 「実際の文字数 + 1」 となります。

2.2 終端文字(ヌル文字 ' ')の重要性

ヌル文字とは?

ヌル文字(' ')は、文字列の終わりを示す特殊な文字です。C言語の文字列を正しく扱うためには、このヌル文字の存在を理解しておく必要があります。

#include <stdio.h>

int main() {
    char str[6] = {'H', 'e', 'l', 'l', 'o', ' '}; // 明示的に終端文字を指定
    printf("%s
", str); // 正しく表示される
    return 0;
}

上記のコードでは、' ' がないと "Hello" の終端が認識されず、意図しない動作が発生する可能性があります

ヌル文字がない場合の問題

以下のように、終端文字を忘れてしまうと、メモリの異常な動作を引き起こす可能性があります。

#include <stdio.h>

int main() {
    char str[5] = {'H', 'e', 'l', 'l', 'o'}; // ヌル文字を含めていない
    printf("%s
", str); // 予期しない動作が発生する可能性
    return 0;
}

エラーの原因

  • printf("%s", str);ヌル文字 ' ' を見つけるまで文字を出力し続ける
  • もし ' ' がなければ、メモリ上の他のデータが出力される可能性がある。

2.3 文字列の正しい定義方法

方法① 文字列リテラルを使用する

最も一般的な文字列の定義方法は、文字列リテラルを使うことです。

char str[] = "Hello";

この方法では、Cコンパイラが自動的にヌル文字 ' ' を追加してくれるため、特別な処理は不要です。

方法② 明示的に配列を定義する

手動で ' ' を含めて定義する場合は、次のように記述します。

char str[6] = {'H', 'e', 'l', 'l', 'o', ' '};
  • 文字数 +1 のサイズを指定し、最後に ' ' を入れることが重要。
  • もし str[5]' ' を入れ忘れると、予期しない動作が発生する。

2.4 文字列のサイズを確認する方法

文字列の長さ(文字数)を取得するためには、strlen 関数を使用します。

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

int main() {
    char str[] = "Hello";
    printf("文字列の長さ: %lu
", strlen(str)); // 5 を出力(ヌル文字を含まない)
    return 0;
}

strlen の動作

  • strlen は、ヌル文字 ' ' が現れるまでの文字数を数える
  • sizeof(str) と異なり、配列のサイズではなく「実際の文字列の長さ」を取得する。

2.5 まとめ

  1. C言語の文字列は char 配列で表現される
  2. 終端文字(ヌル文字 ' ')が文字列の終わりを示すため、必ず含める必要がある
  3. 文字列の長さを取得するには strlen を使う
  4. 適切な方法で文字列を定義しないと、予期しないエラーが発生する可能性がある

3. C言語で部分文字列を切り出す方法【標準ライブラリ編】

C言語で部分文字列を切り出すには、標準ライブラリを活用する方法があります。本セクションでは、strncpystrchr などの標準ライブラリ関数を使用して、文字列を部分的に取得する方法を解説します。

3.1 strncpy を使った部分文字列の取得

strncpy は、文字列の一部を別のバッファにコピーする関数です。

strncpy の基本構文

char *strncpy(char *dest, const char *src, size_t n);
  • dest: コピー先のバッファ
  • src: コピー元の文字列
  • n: コピーする最大文字数(' ' を含めない)

基本的な使用例

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

int main() {
    char src[] = "Hello, World!";
    char dest[6];  // 部分文字列を格納するバッファ

    strncpy(dest, src, 5); // 先頭5文字 "Hello" をコピー
    dest[5] = ' ';  // ヌル文字を手動で追加

    printf("部分文字列: %s
", dest);  // "Hello" を出力

    return 0;
}

strncpy の注意点

  1. ヌル文字 ' ' を手動で追加する必要がある
    strncpy は最大 n 文字をコピーするが、' ' を自動的に追加しないため、明示的に dest[n] = ' '; を追加する必要があります。
  2. バッファオーバーフローに注意
    dest のサイズより n が大きいと、バッファを超えて書き込む可能性があります。

3.2 strncpy_s を使った安全な文字列コピー

strncpy_s は、strncpy の安全性を強化したバージョンで、バッファオーバーフローを防ぐことができます。

strncpy_s の基本構文

errno_t strncpy_s(char *dest, rsize_t destsz, const char *src, rsize_t n);
  • dest: コピー先のバッファ
  • destsz: dest のサイズ
  • src: コピー元の文字列
  • n: コピーする最大文字数

使用例

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

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    if (strncpy_s(dest, sizeof(dest), src, 5) == 0) {
        dest[5] = ' ';  // 念のためヌル文字を追加
        printf("部分文字列: %s
", dest);
    } else {
        printf("コピーエラー
");
    }

    return 0;
}

strncpy_s のメリット

  • バッファサイズ (destsz) を指定するため、安全にコピーできる。
  • destsz より n が大きい場合、エラーを返す。

ただし、strncpy_s はC11規格で追加されたため、一部の環境では使用できない点に注意が必要です。

3.3 strchr を使った特定の文字までの切り出し

strchr を使うと、特定の文字の位置を見つけ、その部分までの文字列を取得できます。

strchr の基本構文

char *strchr(const char *str, int c);
  • str: 検索対象の文字列
  • c: 探したい文字(char 型)

使用例

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

int main() {
    char str[] = "Hello, World!";
    char *pos = strchr(str, ','); // ',' の位置を探す

    if (pos != NULL) {
        int length = pos - str; // ',' までの文字数を計算
        char result[20];

        strncpy(result, str, length);
        result[length] = ' '; // ヌル文字を追加

        printf("部分文字列: %s
", result);  // "Hello" を出力
    }

    return 0;
}

ポイント

  • strchr最初に見つかった c のアドレスを返すため、部分文字列の範囲を決定するのに使える。
  • pos - str で部分文字列の長さを計算し、strncpy でコピーすれば特定の文字までの部分文字列が取得可能。

3.4 strstr を使ったキーワード検索と切り出し

strstr は、部分文字列を検索し、そこから先の文字列を取得するのに便利です。

strstr の基本構文

char *strstr(const char *haystack, const char *needle);
  • haystack: 検索対象の文字列
  • needle: 検索する部分文字列

使用例

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

int main() {
    char str[] = "Hello, World!";
    char *pos = strstr(str, "World"); // "World" の位置を検索

    if (pos != NULL) {
        printf("見つかった部分文字列: %s
", pos);
    } else {
        printf("部分文字列が見つかりませんでした。
");
    }

    return 0;
}

ポイント

  • strstr は最初に見つかった needle のポインタを返す。
  • NULL が返る場合、needlehaystack に存在しない。

3.5 まとめ

  1. strncpy を使うと、部分文字列を安全にコピーできるが、ヌル文字を手動で追加する必要がある。
  2. strncpy_sdestsz を指定でき、安全性が向上する。
  3. strchr を使えば、特定の文字までの部分文字列を取得できる。
  4. strstr を使えば、特定のキーワードの位置を取得し、そこから先を切り出せる。

標準ライブラリを活用することで、C言語での文字列処理をシンプルかつ安全に実装できます。

4. C言語で部分文字列を切り出す方法【自作関数編】

標準ライブラリを活用すれば、基本的な部分文字列の切り出しは可能ですが、場合によってはより柔軟な方法が求められることがあります。そこで、本セクションでは自作関数を使った部分文字列の切り出しについて解説します。

4.1 自作関数を作るメリット

標準ライブラリを使うと、部分文字列のコピーや検索ができますが、以下のような問題点があります。

  • strncpy はヌル文字 ' ' を自動追加しない
  • strchrstrstr は部分的な検索しかできない
  • より柔軟な文字列の操作が難しい

そのため、特定の用途に応じてカスタマイズできる自作関数を作成するのが有効です。

4.2 基本的な部分文字列抽出関数

まず、指定した位置から文字列を切り出す基本的な関数を作成します。

関数の仕様

  • 引数
  • const char *source(元の文字列)
  • int start(開始位置)
  • int length(切り出す文字数)
  • char *dest(切り出した文字列を格納するバッファ)
  • 処理内容
  • start から length 分の文字列を dest にコピーする
  • ' ' を自動的に追加

実装コード

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

void substring(const char *source, int start, int length, char *dest) {
    int i;
    for (i = 0; i < length && source[start + i] != ' '; i++) {
        dest[i] = source[start + i];
    }
    dest[i] = ' '; // ヌル文字を追加
}

int main() {
    char text[] = "Hello, World!";
    char result[10];

    substring(text, 7, 5, result); // "World" を切り出す
    printf("部分文字列: %s
", result);

    return 0;
}

ポイント

  • for ループで指定された length の文字をコピー。
  • ' ' に到達した場合はループを終了。
  • dest[i] = ' '; を追加して必ずヌル文字を末尾に配置

4.3 malloc を使用した動的な部分文字列取得

上記の関数では、dest のサイズを事前に確保する必要があります。しかし、必要なサイズを動的に確保できれば、より汎用的な関数になります。

関数の仕様

  • 必要なメモリを malloc で確保
  • start から length 文字分の部分文字列を新しいバッファにコピー
  • 呼び出し元で free する必要がある

実装コード

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

char *substring_dynamic(const char *source, int start, int length) {
    char *dest = (char *)malloc(length + 1); // ヌル文字のために+1
    if (dest == NULL) {
        return NULL; // メモリ確保失敗時
    }

    int i;
    for (i = 0; i < length && source[start + i] != ' '; i++) {
        dest[i] = source[start + i];
    }
    dest[i] = ' ';

    return dest;
}

int main() {
    char text[] = "Hello, World!";
    char *result = substring_dynamic(text, 7, 5);

    if (result != NULL) {
        printf("部分文字列: %s
", result);
        free(result); // メモリ解放
    } else {
        printf("メモリ確保に失敗しました。
");
    }

    return 0;
}

ポイント

  • malloc動的にメモリを確保するため、バッファのサイズを気にせずに済む。
  • 使用後は free(result); でメモリを解放する必要がある。

4.4 マルチバイト文字(日本語)対応

日本語(UTF-8 などのマルチバイト文字)を扱う場合、1文字が 1バイトとは限らない ため、単純な substring 関数では正しく動作しません。

マルチバイト文字を考慮した実装

  • mbstowcs を使用し、マルチバイト文字列をワイド文字列(wchar_t)に変換
  • wcsncpy を使用して部分文字列を取得
  • wcstombs で再びマルチバイト文字列に戻す

実装コード(UTF-8 対応)

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

void substring_utf8(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, ""); // ロケールを設定

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // UTF-8 文字列をワイド文字列に変換

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // ワイド文字列で切り出し
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256); // マルチバイト文字列に戻す
}

int main() {
    char text[] = "こんにちは、世界!"; // UTF-8 の文字列
    char result[20];

    substring_utf8(text, 5, 3, result); // "世界" を取得
    printf("部分文字列: %s
", result);

    return 0;
}

ポイント

  • setlocale(LC_ALL, ""); でロケールを設定し、マルチバイト対応。
  • mbstowcs でマルチバイト文字列をワイド文字列に変換。
  • wcsncpy で部分文字列を取得後、wcstombs でマルチバイトに戻す。

4.5 まとめ

  1. substring を自作すれば、柔軟に部分文字列を取得できる。
  2. 動的メモリ確保 (malloc) を利用すると、可変サイズの部分文字列を取得可能。
  3. マルチバイト文字(日本語)を扱う場合は、mbstowcs / wcstombs を活用する。

標準ライブラリの strncpystrchr では対応しづらい場合、自作関数を作成することで、C言語の文字列処理をより強力にすることができます。

5. 文字コードごとの文字列切り出し方法

C言語では、文字コードの違いに注意しないと、文字列の切り出し処理が正しく動作しないことがあります。特に、日本語のようなマルチバイト文字(UTF-8、Shift_JIS、EUC-JPなど)を扱う場合、1文字=1バイトではないため、単純な strncpysubstring 関数では適切に処理できません。

本セクションでは、文字コードごとの文字列切り出し方法について詳しく解説します。

5.1 ASCII(1バイト文字)の場合

基本的な部分文字列取得

ASCII 文字は 1文字 = 1バイト なので、strncpysubstring 関数で簡単に処理できます。

実装例
#include <stdio.h>
#include <string.h>

void substring_ascii(const char *source, int start, int length, char *dest) {
    strncpy(dest, source + start, length);
    dest[length] = ' '; // ヌル文字を追加
}

int main() {
    char text[] = "Hello, World!";
    char result[6];

    substring_ascii(text, 7, 5, result); // "World" を取得
    printf("部分文字列: %s
", result);

    return 0;
}

ポイント

  • ASCII 文字(英数字のみ)の場合は strncpy で十分対応可能
  • ' '(ヌル文字)を必ず追加する

5.2 UTF-8(マルチバイト文字)の場合

UTF-8 の特性

UTF-8 では、1文字のバイト数が 1~4バイト と可変のため、単純に strncpy を使うと文字の途中で切れてしまう可能性があります。

正しい処理方法

C言語では、UTF-8 を安全に処理するには mbstowcs を使ってワイド文字列(wchar_t)に変換し、部分文字列を取得する方法が推奨されます。

UTF-8 に対応した部分文字列取得
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

void substring_utf8(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, ""); // ロケールを設定

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // マルチバイト文字列をワイド文字列に変換

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // 部分文字列を取得
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256); // ワイド文字列を再びマルチバイトに変換
}

int main() {
    char text[] = "こんにちは、世界!"; // UTF-8 文字列
    char result[20];

    substring_utf8(text, 5, 3, result); // "世界" を取得
    printf("部分文字列: %s
", result);

    return 0;
}

ポイント

  • setlocale(LC_ALL, ""); でロケール設定をしないと、マルチバイト文字が正しく処理されない。
  • mbstowcs でマルチバイト文字列を wchar_t に変換し、wcsncpy で安全に処理する。
  • wcstombs で再びマルチバイト文字列に戻す。

5.3 Shift_JIS(マルチバイト文字)の場合

Shift_JIS の特性

Shift_JIS では、1文字が1バイトまたは2バイト になるため、単純な strncpy では文字化けの原因になります。

Shift_JIS に対応した部分文字列取得

Shift_JIS の場合も ワイド文字列に変換して処理する方法 が推奨されます。

Shift_JIS での実装
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

void substring_sjis(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, "Japanese"); // Shift_JIS を処理するロケールを設定

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256);

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length);
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256);
}

int main() {
    char text[] = "こんにちは、世界!"; // Shift_JIS 文字列(環境による)
    char result[20];

    substring_sjis(text, 5, 3, result);
    printf("部分文字列: %s
", result);

    return 0;
}

ポイント

  • Shift_JIS を正しく処理するには setlocale(LC_ALL, "Japanese"); を設定。
  • mbstowcswcstombs を使用して、安全に文字列を処理。

5.4 EUC-JP(マルチバイト文字)の場合

EUC-JP の特性

EUC-JP も Shift_JIS と同様に、1文字のバイト数が異なるため、ワイド文字を利用した変換処理が必要 になります。

EUC-JP に対応した部分文字列取得

#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>

void substring_eucjp(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, "ja_JP.eucJP"); // EUC-JP を処理するロケールを設定

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256);

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length);
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256);
}

int main() {
    char text[] = "こんにちは、世界!"; // EUC-JP 文字列(環境による)
    char result[20];

    substring_eucjp(text, 5, 3, result);
    printf("部分文字列: %s
", result);

    return 0;
}

ポイント

  • setlocale(LC_ALL, "ja_JP.eucJP"); で EUC-JP のロケールを設定。
  • mbstowcs / wcstombs を使い、マルチバイト文字を正しく処理。

5.5 まとめ

文字コードバイト数推奨する処理方法
ASCII1バイトstrncpy でOK
UTF-81~4バイトmbstowcs / wcstombs を使用
Shift_JIS1 or 2バイトmbstowcs / wcstombs を使用
EUC-JP1 or 2バイトmbstowcs / wcstombs を使用
  • ASCII 文字のみなら strncpy でOK
  • UTF-8, Shift_JIS, EUC-JP の場合は mbstowcs / wcstombs を使用
  • 環境に応じて setlocale(LC_ALL, "..."); を適切に設定する

6. C言語で文字列を分割する方法

文字列を分割する処理は、CSVデータの解析、コマンドラインの引数処理、ログデータの解析など、多くの場面で必要になります。C言語では、strtokstrtok_r などの標準ライブラリ関数を使う方法や、自作関数を作る方法があります。

本セクションでは、文字列を特定の区切り文字で分割する方法について詳しく解説します。

6.1 strtok を使った文字列分割

strtok は、指定した区切り文字(デリミタ)で文字列を分割する関数です。

基本構文

char *strtok(char *str, const char *delim);
  • str: 分割対象の文字列(最初の呼び出し時に指定)
  • delim: 区切り文字(複数指定可能)
  • 戻り値: 最初のトークン(分割された最初の部分)
  • 注意点: strtok は元の文字列を改変する(区切り文字を ' ' に変える)

使用例:カンマ , で文字列を分割

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

int main() {
    char str[] = "apple,banana,orange,grape"; // 分割する文字列
    char *token = strtok(str, ","); // 最初のトークンを取得

    while (token != NULL) {
        printf("トークン: %s
", token);
        token = strtok(NULL, ","); // 次のトークンを取得
    }

    return 0;
}

実行結果

トークン: apple
トークン: banana
トークン: orange
トークン: grape

strtok の注意点

  1. 元の文字列を変更する
  • strtok は、区切り文字を ' ' に書き換える ため、元の文字列が失われる。
  1. スレッドセーフではない
  • strtokグローバルな静的変数を内部で使用するため、マルチスレッド環境では使わない方がよい。

6.2 strtok_r を使ったスレッドセーフな文字列分割

strtok_r は、strtok のスレッドセーフ版であり、状態を saveptr に保存するため、マルチスレッド環境でも安全に使用可能です。

基本構文

char *strtok_r(char *str, const char *delim, char **saveptr);
  • str: 分割対象の文字列(最初の呼び出し時に指定)
  • delim: 区切り文字(複数指定可能)
  • saveptr: 内部状態を保持するポインタ(呼び出しごとに更新)

使用例:スペース で文字列を分割

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

int main() {
    char str[] = "Hello World from C"; // 分割する文字列
    char *token;
    char *saveptr; // 内部状態を保存するポインタ

    token = strtok_r(str, " ", &saveptr); // 最初のトークンを取得
    while (token != NULL) {
        printf("トークン: %s
", token);
        token = strtok_r(NULL, " ", &saveptr); // 次のトークンを取得
    }

    return 0;
}

strtok_r のメリット

  • スレッドセーフ
  • 複数の文字列を並行処理できる

6.3 自作関数で文字列を分割する(strtok を使わない方法)

strtok は元の文字列を変更するため、変更せずに文字列を分割する自作関数を作成することも可能です。

自作関数の仕様

  • 入力
  • const char *source(元の文字列)
  • const char delim(区切り文字)
  • char tokens[][50](分割された文字列を格納する配列)
  • 処理
  • source をコピーし、オリジナルを変更しないようにする
  • delim に基づいて tokens に分割結果を格納

実装コード

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

void split_string(const char *source, char delim, char tokens[][50], int *count) {
    int i = 0, j = 0, token_index = 0;

    while (source[i] != ' ') {
        if (source[i] == delim) {
            tokens[token_index][j] = ' ';
            token_index++;
            j = 0;
        } else {
            tokens[token_index][j] = source[i];
            j++;
        }
        i++;
    }
    tokens[token_index][j] = ' ';
    *count = token_index + 1;
}

int main() {
    char text[] = "dog,cat,bird,fish";
    char tokens[10][50]; // 最大10個の単語を格納可能
    int count;

    split_string(text, ',', tokens, &count);

    for (int i = 0; i < count; i++) {
        printf("トークン: %s
", tokens[i]);
    }

    return 0;
}

実行結果

トークン: dog
トークン: cat
トークン: bird
トークン: fish

ポイント

  • source を変更せずにコピーを作成して処理する。
  • tokens 配列に分割結果を格納し、オリジナルの文字列を保持する。

6.4 文字列分割の応用(CSVデータの処理)

CSV(カンマ区切り)データを strtok を使って解析することができます。

CSVデータ解析の例

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

int main() {
    char csv[] = "Alice,24,Female
Bob,30,Male
Charlie,28,Male"; // CSVデータ
    char *line = strtok(csv, "
"); // 1行ずつ処理

    while (line != NULL) {
        char *name = strtok(line, ",");
        char *age = strtok(NULL, ",");
        char *gender = strtok(NULL, ",");

        printf("名前: %s, 年齢: %s, 性別: %s
", name, age, gender);

        line = strtok(NULL, "
");
    }

    return 0;
}

実行結果

名前: Alice, 年齢: 24, 性別: Female
名前: Bob, 年齢: 30, 性別: Male
名前: Charlie, 年齢: 28, 性別: Male

6.5 まとめ

方法メリットデメリット
strtok簡単に分割できる元の文字列を変更する
strtok_rスレッドセーフ使い方が少し複雑
自作関数元の文字列を変更しないコードが長くなる
CSV解析データ処理に便利strtok の制限に注意

結論

  • 単純な分割なら strtok
  • マルチスレッドなら strtok_r
  • オリジナルを変更したくないなら自作関数
  • CSVデータ解析にも応用可能

次のセクションでは、「応用例: 特定の文字の前後を抽出する方法」について詳しく解説します。

7. 応用例: 特定の文字の前後を抽出する方法

文字列の処理では、特定の文字やキーワードの前後を抽出する操作が必要になることがよくあります。たとえば、以下のようなケースが考えられます。

  • URL からドメイン部分のみを取得
  • ファイルパスからファイル名を抽出
  • 特定のタグや記号の前後の文字列を取得

C言語では、strchrstrstr を利用することで、こうした処理を実現できます。また、より柔軟な処理が必要な場合は、自作関数を作成する方法も有効です。

7.1 strchr を使って特定の文字の前の文字列を取得

strchr を使うと、特定の文字(最初に見つかったもの)の位置を特定できます。

基本構文

char *strchr(const char *str, int c);
  • str: 検索対象の文字列
  • c: 探したい文字(char 型)

strchrc を見つけた場合、そのアドレスを返します。

使用例: ファイルパスからファイル名を取得

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

void get_filename(const char *path, char *filename) {
    char *pos = strrchr(path, '/'); // 最後の '/' を検索

    if (pos != NULL) {
        strcpy(filename, pos + 1); // '/' の次の位置からコピー
    } else {
        strcpy(filename, path); // '/' がなければそのままコピー
    }
}

int main() {
    char path[] = "/home/user/documents/report.txt";
    char filename[50];

    get_filename(path, filename);
    printf("ファイル名: %s
", filename);

    return 0;
}

実行結果

ファイル名: report.txt

ポイント

  • strrchr を使うことで、最後に出現した特定の文字(/)の位置を取得できる。
  • pos + 1 で、スラッシュの次の文字列をコピーすれば、ファイル名だけを取得可能

7.2 strstr を使って特定のキーワードの後の文字列を取得

strstr を使うと、特定の文字列(キーワード)を検索し、その位置から先の文字列を取得できます。

基本構文

char *strstr(const char *haystack, const char *needle);
  • haystack: 検索対象の文字列
  • needle: 検索する部分文字列

strstrneedle を見つけた場合、その位置のアドレスを返します。

使用例: URL からドメインを取得

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

void get_domain(const char *url, char *domain) {
    char *pos = strstr(url, "://"); // "://" の位置を検索

    if (pos != NULL) {
        strcpy(domain, pos + 3); // "://" の次からコピー
    } else {
        strcpy(domain, url); // "://" がない場合、そのままコピー
    }
}

int main() {
    char url[] = "https://www.example.com/page.html";
    char domain[50];

    get_domain(url, domain);
    printf("ドメイン部分: %s
", domain);

    return 0;
}

実行結果

ドメイン部分: www.example.com/page.html

ポイント

  • strstr を使って "https://""http://""//" 以降を取得。
  • pos + 3:// の次からコピー。

7.3 strchr を使って特定の文字の前後の部分を分割

strchr を活用すれば、特定の文字の前後の文字列を分割して取得できます。

使用例: メールアドレスからユーザー名とドメインを分離

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

void split_email(const char *email, char *username, char *domain) {
    char *pos = strchr(email, '@'); // '@' の位置を検索

    if (pos != NULL) {
        strncpy(username, email, pos - email); // '@' の前をコピー
        username[pos - email] = ' '; // ヌル文字を追加
        strcpy(domain, pos + 1); // '@' の次の部分をコピー
    }
}

int main() {
    char email[] = "user@example.com";
    char username[50], domain[50];

    split_email(email, username, domain);
    printf("ユーザー名: %s
", username);
    printf("ドメイン: %s
", domain);

    return 0;
}

実行結果

ユーザー名: user
ドメイン: example.com

ポイント

  • strchr'@' の位置を検索。
  • strncpy'@' の前の部分をコピーし、ヌル文字を追加
  • strcpy'@' の後の部分をコピー

7.4 応用: HTMLタグ内の特定の属性を抽出

HTML タグの中から特定の属性を取得する場合も、strstr を活用できます。

使用例: <a href="URL"> から URL を取得

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

void get_href(const char *html, char *url) {
    char *start = strstr(html, "href=""); // href=" の位置を検索
    if (start != NULL) {
        start += 6; // href=" の後ろに移動
        char *end = strchr(start, '"'); // 次の " を検索
        if (end != NULL) {
            strncpy(url, start, end - start);
            url[end - start] = ' '; // ヌル文字を追加
        }
    }
}

int main() {
    char html[] = "<a href="https://example.com">Click Here</a>";
    char url[100];

    get_href(html, url);
    printf("抽出されたURL: %s
", url);

    return 0;
}

実行結果

抽出されたURL: https://example.com

ポイント

  • strstr"href="" の位置を検索し、6文字分移動。
  • strchr"(クオート)の位置を検索し、範囲を決定。

7.5 まとめ

処理内容使用関数メリット
特定の文字の前を取得strchr / strrchrシンプルで高速
特定の文字の後を取得strstrキーワードの検索が可能
特定の文字で前後を分割strchr + strncpyユーザー名・ドメイン分割などに便利
HTMLタグの属性取得strstr + strchrWebスクレイピングに応用可能

結論

  • strchrstrstr を活用すると、特定の文字・キーワードの前後を簡単に取得できる
  • ファイルパスの処理、URLの解析、メールアドレスの分割など、多くの場面で役立つ
  • Webスクレイピングのような高度な処理にも応用可能

8. まとめ

本記事では、C言語における文字列の切り出し方法について、基本から応用まで詳しく解説しました。ここで、各セクションの重要ポイントを振り返り、用途別に最適な方法を整理します。

8.1 記事の振り返り

セクション内容重要ポイント
C言語の文字列の基本C言語では文字列は char 配列として扱われ、終端文字 ' ' が重要文字列を扱う際は ヌル終端を忘れないこと
標準ライブラリでの切り出しstrncpystrchr などを活用strncpyヌル終端を手動で追加 する必要あり
自作関数による切り出し柔軟な substring 関数を作成malloc を使うと 可変長の部分文字列取得が可能
文字コードごとの処理UTF-8, Shift_JIS, EUC-JP への対応方法mbstowcs / wcstombs を使って ワイド文字に変換するのが安全
文字列の分割方法strtok, strtok_r, 自作関数による分割strtok元の文字列を変更 するので注意
特定の文字の前後を抽出strchr, strstr によるデータ取得ファイル名の取得、URL解析、HTML解析 に応用できる

8.2 用途別の最適な方法

1. 部分文字列の切り出し

使用場面最適な方法
一定の長さの文字列を取得したいstrncpy or substring()
安全な切り出しをしたいstrncpy_s(C11以降)
マルチバイト文字(UTF-8, Shift_JIS, EUC-JP)を扱うmbstowcs / wcstombs

2. 文字列の分割

使用場面最適な方法
シンプルに文字列を区切りたいstrtok
スレッドセーフな分割をしたいstrtok_r
元の文字列を変更せずに分割したい自作関数(split_string()

3. 特定の文字の前後を取得

使用場面最適な方法
ファイルパスからファイル名を取得strrchr(path, '/')
URLからドメイン部分を取得strstr(url, "://")
メールアドレスからユーザー名とドメインを分離strchr(email, '@')
HTMLタグから属性値を取得strstr(tag, "href="") + strchr(tag, '"')

8.3 C言語の文字列処理の注意点

1. ヌル終端 ' ' の管理を徹底する

C言語の文字列処理では、終端文字 ' ' を適切に管理することが最も重要です。特に strncpystrchr を使用する際は、ヌル文字を手動で追加するように注意してください。

安全な文字列コピーの例
#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    strncpy(dest, src, 5);
    dest[5] = ' '; // 安全のためにヌル終端を追加

    printf("部分文字列: %s
", dest);

    return 0;
}

2. バッファオーバーフローに注意する

C言語の文字列操作では、配列の範囲外にアクセスしないように慎重に実装する必要があります。特に、strncpy を使う際はコピーするバイト数を制御することが重要です。

安全な文字列コピーの例

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

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    strncpy(dest, src, sizeof(dest) - 1);
    dest[5] = ' '; // ヌル文字を明示的に追加

    printf("部分文字列: %s
", dest);
    return 0;
}

3. マルチバイト文字の処理には mbstowcs を使う

UTF-8 や Shift_JIS などのマルチバイト文字を扱う場合、単純に strncpystrlen が正しく動作しない。

そのため、マルチバイト文字を扱う場合は、一度 mbstowcs でワイド文字列に変換し、適切に処理することが推奨されます。

#include <stdio.h>
#include <wchar.h>
#include <locale.h>

int main() {
    setlocale(LC_ALL, ""); // ロケールを設定

    char text[] = "こんにちは、世界!"; // UTF-8
    wchar_t wtext[256];

    mbstowcs(wtext, text, 256); // ワイド文字列に変換

    printf("変換後のワイド文字列: %ls
", wtext);

    return 0;
}

4. バッファサイズの管理

文字列処理では、必要なメモリサイズを事前に計算し、バッファのオーバーフローを防ぐことが重要です。特に、malloc を使って動的メモリを確保する際には、そのサイズを正確に把握するようにしましょう。

8.4 さらなる学習に向けて

C言語の文字列処理は、プログラムの安全性や可読性を向上させる重要なスキルです。本記事で紹介した内容を踏まえて、さらに以下のトピックも学習すると、より高度な文字列処理が可能になります。

学習を深めるためのトピック

  1. 正規表現(regex)(C言語の外部ライブラリで対応可能)
  2. ファイル操作(fgets, fscanf を使った文字列処理)
  3. メモリ管理(malloc, realloc を使った動的文字列処理)
  4. データ解析(JSON, XML の解析方法)

8.5 まとめ

  1. C言語の文字列は char 配列で管理されるため、終端文字 ' ' の扱いが重要
  2. 部分文字列の切り出しには strncpy, substring(), malloc を使う
  3. 文字列の分割には strtok / strtok_r / 自作関数を活用
  4. 特定の文字の前後を取得する場合は strchr, strstr を活用
  5. マルチバイト文字(日本語)を扱う場合は、mbstowcs を利用
  6. 安全な文字列処理を心がけ、バッファオーバーフローに注意

本記事の内容を活用すれば、C言語での実用的な文字列処理が可能になります。基本的な関数を理解した上で、自作関数や応用処理に挑戦し、より効率的なコードを書けるようになりましょう!

侍エンジニア塾