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

目次

1. はじめに

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

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

この記事で学べること

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

  • C言語の文字列の基本概念と終端文字 '\0' の役割
  • 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!"; // Define a string literal as an array
    printf("%s ", str); // Output the string
    return 0;
}

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

文字列の内部構造

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

インデックス012345
文字Hello\0

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

2.2 終端文字(ヌル文字 '

ヌル文字とは?

'
)の重要性

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

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

#include <stdio.h>

int main() {
    char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // Explicitly specify the null terminator
    printf("%s ", str);                            // Display correctly
    return 0;
}

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

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

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

#include <stdio.h>

int main() {
    char str[5] = {'H', 'e', 'l', 'l', 'o'}; // Does not include the null terminator
    printf("%s ", str);                      // May cause unexpected behavior
    return 0;
}
}

エラーの原因

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

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

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

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

char str[] = "Hello";

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

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

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

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

strlen の動作

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

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

int main() {
    char str[] = "Hello";
    printf("Length of the string: %lu\n", strlen(str)); // Outputs 5 (does not include the null terminator)
    return 0;
}
}

2.5 まとめ

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

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

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

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

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

strncpy の基本構文

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

基本的な使用例

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

strncpy の注意点

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

int main() {
    char src[] = "Hello, World!";
    char dest[6];  // Buffer to store the substring

    strncpy(dest, src, 5); // Copy the first 5 characters "Hello"
    dest[5] = '\0';        // Manually add the null terminator

    printf("Substring: %s\n", dest);  // Output "Hello"

    return 0;
}
}

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

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

strncpy_s の基本構文

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

使用例

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

strncpy_s のメリット

#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] = '\0';  // Add null terminator just in case
        printf("Substring: %s\n", dest);
    } else {
        printf("Copy error\n");
    }

    return 0;
}
}

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

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

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

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, ','); // Find the position of ','

    if (pos != NULL) {
        int length = pos - str; // Calculate the number of characters up to ','
        char result[20];

        strncpy(result, str, length);
        result[length] = '\0'; // Add the null terminator

        printf("Substring: %s\n", result);  // Output "Hello"
    }

    return 0;
}

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

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

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"); // Search for the position of "World"

    if (pos != NULL) {
        printf("Found substring: %s\n", pos);
    } else {
        printf("Substring not found.\n");
    }

    return 0;
}

3.5 まとめ

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

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

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

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

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

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

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

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

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

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

関数の仕様

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

実装コード

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

ポイント

#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] != '\0'; i++) {
        dest[i] = source[start + i];
    }
    dest[i] = '\0'; // Add null terminator
}

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

    substring(text, 7, 5, result); // Extract "World"
    printf("Substring: %s\n", result);

    return 0;
}

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

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

関数の仕様

上記の関数では、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 for the null terminator
    if (dest == NULL) {
        return NULL; // Memory allocation failed
    }

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

    return dest;
}

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

    if (result != NULL) {
        printf("Substring: %s\n", result);
        free(result); // Free allocated memory
    } else {
        printf("Memory allocation failed.\n");
    }

    return 0;
}

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

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

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

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

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

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

ポイント

#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, ""); // Set the locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convert UTF-8 string to wide-character string

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Extract substring in wide characters
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Convert back to multibyte string
}

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

    substring_utf8(text, 5, 3, result); // Extract "世界"
    printf("Substring: %s\n", result);

    return 0;
}

4.5 まとめ

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

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

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

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

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

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

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

基本的な部分文字列取得

実装例

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

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

#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] = '\0'; // Add null terminator
}

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

    substring_ascii(text, 7, 5, result); // Extract "World"
    printf("Substring: %s\n", result);

    return 0;
}

ポイント

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

UTF-8 の特性

正しい処理方法

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

UTF-8 に対応した部分文字列取得

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

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

#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, ""); // Set the locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convert multibyte string to wide-character string

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Get the substring
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Convert wide-character string back to multibyte
}

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

    substring_utf8(text, 5, 3, result); // Extract "世界"
    printf("Substring: %s\n", result);

    return 0;
}

ポイント

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

Shift_JIS の特性

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

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

Shift_JIS での実装

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

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

#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"); // Set locale to handle Shift_JIS

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convert multibyte string (Shift_JIS) to wide-character string

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Extract substring
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Convert wide-character string back to multibyte (Shift_JIS)
}

int main() {
    char text[] = "こんにちは、世界!"; // Shift_JIS string (depending on environment)
    char result[20];

    substring_sjis(text, 5, 3, result); // Extract "世界"
    printf("Substring: %s\n", result);

    return 0;
}

ポイント

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

EUC-JP の特性

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

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

5.5 まとめ

#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"); // Set locale to handle EUC-JP

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convert multibyte string (EUC-JP) to wide-character string

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Extract substring
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Convert wide-character string back to multibyte (EUC-JP)
}

int main() {
    char text[] = "こんにちは、世界!"; // EUC-JP string (depending on environment)
    char result[20];

    substring_eucjp(text, 5, 3, result); // Extract "世界"
    printf("Substring: %s\n", result);

    return 0;
}

ポイント

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

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

文字コードバイト数推奨する処理方法
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.1 strtok を使った文字列分割

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

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

基本構文

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

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

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

実行結果

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


int main() {
    char str[] = "apple,banana,orange,grape"; // String to be split
    char *token = strtok(str, ",");            // Get the first token

    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, ",");             // Get the next token
    }

    return 0;
}

strtok の注意点

token: apple
token: banana
token: orange
token: grape

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

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

基本構文

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

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

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

strtok_r のメリット

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

int main() {
    char str[] = "Hello World from C"; // String to be split
    char *token;
    char *saveptr; // Pointer to store internal state

    token = strtok_r(str, " ", &saveptr); // Get the first token
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok_r(NULL, " ", &saveptr); // Get the next token
    }

    return 0;
}

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] != '\0') {
        if (source[i] == delim) {
            tokens[token_index][j] = '\0';
            token_index++;
            j = 0;
        } else {
            tokens[token_index][j] = source[i];
            j++;
        }
        i++;
    }
    tokens[token_index][j] = '\0';
    *count = token_index + 1;
}

int main() {
    char text[] = "dog,cat,bird,fish";
    char tokens[10][50]; // Can store up to 10 words
    int count;

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

    for (int i = 0; i < count; i++) {
        printf("Token: %s\n", tokens[i]);
    }

    return 0;
}

ポイント

Token: dog
Token: cat
Token: bird
Token: fish

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

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

CSVデータ解析の例

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

実行結果

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

int main() {
    char csv[] = "Alice,24,Female\nBob,30,Male\nCharlie,28,Male"; // CSV data
    char *line = strtok(csv, "\n"); // Process line by line

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

        printf("Name: %s, Age: %s, Gender: %s\n", name, age, gender);

        line = strtok(NULL, "\n");
    }

    return 0;
}

6.5 まとめ

Name: Alice, Age: 24, Gender: Female
Name: Bob, Age: 30, Gender: Male
Name: Charlie, Age: 28, Gender: Male

結論

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

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

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

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

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

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

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

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

基本構文

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, '/'); // Search for the last '/'

    if (pos != NULL) {
        strcpy(filename, pos + 1); // Copy from the character after '/'
    } else {
        strcpy(filename, path); // If no '/', copy the whole path
    }
}

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

    get_filename(path, filename);
    printf("Filename: %s\n", filename);

    return 0;
}

ポイント

Filename: report.txt

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

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

基本構文

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

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

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

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

実行結果

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

void get_domain(const char *url, char *domain) {
    char *pos = strstr(url, "://"); // Search for the position of "://"

    if (pos != NULL) {
        strcpy(domain, pos + 3); // Copy from the character after "://"
    } else {
        strcpy(domain, url); // If "://" is not found, copy the entire string
    }
}

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

    get_domain(url, domain);
    printf("Domain part: %s\n", domain);

    return 0;
}

ポイント

Domein part: www.example.com/page.html

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

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

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

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

実行結果

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

void split_email(const char *email, char *username, char *domain) {
    char *pos = strchr(email, '@'); // Search for the position of '@'

    if (pos != NULL) {
        strncpy(username, email, pos - email); // Copy the part before '@'
        username[pos - email] = '\0';          // Add null terminator
        strcpy(domain, pos + 1);               // Copy the part after '@'
    }
}

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

    split_email(email, username, domain);
    printf("Username: %s\n", username);
    printf("Domain: %s\n", domain);

    return 0;
}

ポイント

Username: user
Domain: example.com

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

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

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

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

実行結果

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

void get_href(const char *html, char *url) {
    char *start = strstr(html, "href=\""); // Search for the position of href="
    if (start != NULL) {
        start += 6; // Move past href="
        char *end = strchr(start, '"'); // Search for the next "
        if (end != NULL) {
            strncpy(url, start, end - start);
            url[end - start] = '\0'; // Add null terminator
        }
    }
}

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

    get_href(html, url);
    printf("Extracted URL: %s\n", url);

    return 0;
}

ポイント

Extracted URL: https://example.com

7.5 まとめ

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

結論

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

8. まとめ

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

8.1 記事の振り返り

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

8.2 用途別の最適な方法

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

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

2. 文字列の分割

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

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

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

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

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

1. ヌル終端 '
安全な文字列コピーの例
'
の管理を徹底する

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

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

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

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

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

    strncpy(dest, src, 5);
    dest[5] = '\0'; // Add null terminator for safety

    printf("Substring: %s\n", dest);

    return 0;
}

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

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] = '\0'; // Explicitly add null terminator

    printf("Substring: %s\n", dest);
    return 0;
}

8.4 さらなる学習に向けて

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

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

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

int main() {
    setlocale(LC_ALL, ""); // Set the locale

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

    mbstowcs(wtext, text, 256); // Convert to wide-character string

    printf("Converted wide-character string: %ls\n", wtext);

    return 0;
}

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

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

8.5 まとめ

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

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

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

8.5 まとめ

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

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