初心者向けC++のファイル入出力とシリアルポートの操作方法

C++は多くの分野で使用される強力なプログラミング言語です。本記事では、初心者向けにC++のファイル入出力とシリアルポートの基本操作方法を解説します。具体的なコード例と共に、これらの操作を実際に試して理解を深めることができます。

目次

C++のファイル入出力の基本

C++でファイル入出力を行う際には、標準ライブラリのを使用します。基本的な手順として、ファイルを開き、データを読み書きし、最後にファイルを閉じることが必要です。

ファイルを開く

ファイルを開くには、std::ifstream(読み取り専用)やstd::ofstream(書き込み専用)、またはstd::fstream(読み書き両方)のオブジェクトを使用します。以下に例を示します。

#include <iostream>
#include <fstream>

int main() {
    std::ofstream outFile("example.txt"); // 書き込み用にファイルを開く
    if (outFile.is_open()) {
        // ファイルが正常に開けた場合の処理
    } else {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
    }
    return 0;
}

データを読み書きする

ファイルにデータを書き込むには、<<演算子を使用します。ファイルからデータを読み取るには、>>演算子を使用します。以下に書き込みの例を示します。

outFile << "これはテストメッセージです。" << std::endl;

読み取りの例は以下の通りです。

std::ifstream inFile("example.txt");
if (inFile.is_open()) {
    std::string line;
    while (std::getline(inFile, line)) {
        std::cout << line << std::endl;
    }
    inFile.close();
} else {
    std::cerr << "ファイルを開けませんでした。" << std::endl;
}

ファイルを閉じる

ファイル操作が終わったら、ファイルを閉じることが重要です。これはファイルストリームオブジェクトのclose()メソッドを呼び出すことで行います。

outFile.close();

以上が、C++における基本的なファイル入出力の手順です。

ファイル入出力の例

具体的なファイル入出力のコード例を示し、操作方法を解説します。以下の例では、ファイルからデータを読み込み、読み込んだデータを加工して新しいファイルに書き出すプログラムを示します。

ファイル読み込みと書き込みの例

この例では、”input.txt”からデータを読み込み、読み込んだデータを大文字に変換して”output.txt”に書き込みます。

#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>

int main() {
    std::ifstream inFile("input.txt"); // 読み取り用ファイルを開く
    std::ofstream outFile("output.txt"); // 書き込み用ファイルを開く

    if (!inFile) {
        std::cerr << "入力ファイルを開けませんでした。" << std::endl;
        return 1;
    }
    if (!outFile) {
        std::cerr << "出力ファイルを開けませんでした。" << std::endl;
        return 1;
    }

    std::string line;
    while (std::getline(inFile, line)) {
        std::transform(line.begin(), line.end(), line.begin(), ::toupper); // 大文字に変換
        outFile << line << std::endl; // 出力ファイルに書き込み
    }

    inFile.close();
    outFile.close();

    std::cout << "ファイルの処理が完了しました。" << std::endl;
    return 0;
}

コードの詳細説明

上記のプログラムの詳細な説明を行います。

入力ファイルの読み込み

std::ifstream inFile("input.txt"); // 読み取り用ファイルを開く

この行で”input.txt”というファイルを読み込み用に開きます。ファイルが存在しない場合はエラーメッセージを表示します。

出力ファイルの書き込み

std::ofstream outFile("output.txt"); // 書き込み用ファイルを開く

この行で”output.txt”というファイルを書き込み用に開きます。ファイルが開けない場合はエラーメッセージを表示します。

行単位でデータを処理

while (std::getline(inFile, line)) {
    std::transform(line.begin(), line.end(), line.begin(), ::toupper); // 大文字に変換
    outFile << line << std::endl; // 出力ファイルに書き込み
}

この部分では、入力ファイルから1行ずつデータを読み込み、その行を大文字に変換してから出力ファイルに書き込みます。

ファイルを閉じる

inFile.close();
outFile.close();

最後に、開いたファイルを閉じてリソースを解放します。

この例を実行することで、ファイルの読み書きの基本的な流れを理解することができます。

シリアルポートの基本

シリアルポートは、データ通信に使用されるインターフェースの一つです。特にハードウェアデバイスとの通信で広く利用されます。ここでは、シリアルポートの基本的な概念と、その利用方法について説明します。

シリアルポートとは

シリアルポートは、データを1ビットずつ順次送信する通信方式です。これにより、少ない配線で高速なデータ通信が可能です。一般的に、RS-232規格が使用されます。

シリアルポートの用途

シリアルポートは、以下のような用途に利用されます。

  • コンピュータとモデムの接続
  • 組み込みシステムとの通信
  • センサーデータの取得
  • 工業機器の制御

シリアルポートの通信パラメータ

シリアルポート通信には、以下の主要なパラメータがあります。

  • ボーレート:データ転送速度(ビット/秒)
  • データビット:1回の送信で送られるビット数(通常8ビット)
  • パリティビット:エラーチェックのためのビット(偶数パリティ、奇数パリティなど)
  • ストップビット:データフレームの終わりを示すビット(1ビットまたは2ビット)

シリアルポートのメリットとデメリット

メリット

  • シンプルな配線:少ない配線で通信が可能
  • 互換性:多くのデバイスでサポートされている
  • 信頼性:ノイズ耐性が高く、安定した通信が可能

デメリット

  • 速度の制約:並列通信に比べて速度が遅い
  • 距離の制約:長距離通信には向かない

以上が、シリアルポートの基本的な説明です。次に、具体的な設定方法と初期化手順について解説します。

シリアルポートの設定

シリアルポートを使用するためには、適切な設定と初期化が必要です。C++では、主にWindows APIやPOSIX標準ライブラリを使用して設定を行います。ここでは、基本的な設定方法と初期化手順を説明します。

Windowsでのシリアルポート設定

Windows環境では、CreateFile関数を使用してシリアルポートを開きます。以下は、シリアルポートを設定する例です。

#include <windows.h>
#include <iostream>

int main() {
    HANDLE hSerial;
    hSerial = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "シリアルポートを開けませんでした。" << std::endl;
        return 1;
    }

    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);

    if (!GetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "現在のシリアルポート設定を取得できませんでした。" << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    dcbSerialParams.BaudRate = CBR_9600;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;

    if (!SetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "シリアルポート設定を適用できませんでした。" << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    // ポートが正常に開かれ、設定が適用された場合の処理

    CloseHandle(hSerial); // 使用後にシリアルポートを閉じる
    return 0;
}

POSIX標準でのシリアルポート設定

LinuxやMacOSなどのPOSIX標準環境では、open関数を使用してシリアルポートを開きます。以下は、シリアルポートを設定する例です。

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <iostream>

int main() {
    int serialPort = open("/dev/ttyS0", O_RDWR);

    if (serialPort < 0) {
        std::cerr << "シリアルポートを開けませんでした。" << std::endl;
        return 1;
    }

    termios tty;
    if (tcgetattr(serialPort, &tty) != 0) {
        std::cerr << "現在のシリアルポート設定を取得できませんでした。" << std::endl;
        close(serialPort);
        return 1;
    }

    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);

    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_iflag &= ~IGNBRK;
    tty.c_lflag = 0;
    tty.c_oflag = 0;
    tty.c_cc[VMIN]  = 0;
    tty.c_cc[VTIME] = 5;

    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~(PARENB | PARODD);
    tty.c_cflag |= 0;
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;

    if (tcsetattr(serialPort, TCSANOW, &tty) != 0) {
        std::cerr << "シリアルポート設定を適用できませんでした。" << std::endl;
        close(serialPort);
        return 1;
    }

    // ポートが正常に開かれ、設定が適用された場合の処理

    close(serialPort); // 使用後にシリアルポートを閉じる
    return 0;
}

以上が、シリアルポートの基本設定方法と初期化手順です。次に、シリアルポートからのデータの読み書き方法を解説します。

シリアルポートの読み書き

シリアルポートからデータを読み取ったり、データを書き込んだりする方法を説明します。ここでは、Windows環境とPOSIX標準環境での操作方法を示します。

Windowsでのシリアルポートの読み書き

Windows環境では、ReadFile関数とWriteFile関数を使用してシリアルポートの読み書きを行います。以下にその例を示します。

#include <windows.h>
#include <iostream>

int main() {
    HANDLE hSerial;
    hSerial = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "シリアルポートを開けませんでした。" << std::endl;
        return 1;
    }

    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    GetCommState(hSerial, &dcbSerialParams);
    dcbSerialParams.BaudRate = CBR_9600;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;
    SetCommState(hSerial, &dcbSerialParams);

    // データの書き込み
    const char *data = "Hello, Serial Port!";
    DWORD bytesWritten;
    if (!WriteFile(hSerial, data, strlen(data), &bytesWritten, NULL)) {
        std::cerr << "データを書き込めませんでした。" << std::endl;
    }

    // データの読み込み
    char buffer[128];
    DWORD bytesRead;
    if (ReadFile(hSerial, buffer, sizeof(buffer), &bytesRead, NULL)) {
        std::cout << "読み取ったデータ: " << std::string(buffer, bytesRead) << std::endl;
    } else {
        std::cerr << "データを読み取れませんでした。" << std::endl;
    }

    CloseHandle(hSerial);
    return 0;
}

POSIX標準でのシリアルポートの読み書き

LinuxやMacOSなどのPOSIX標準環境では、read関数とwrite関数を使用してシリアルポートの読み書きを行います。以下にその例を示します。

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <iostream>

int main() {
    int serialPort = open("/dev/ttyS0", O_RDWR);

    if (serialPort < 0) {
        std::cerr << "シリアルポートを開けませんでした。" << std::endl;
        return 1;
    }

    termios tty;
    tcgetattr(serialPort, &tty);
    cfsetispeed(&tty, B9600);
    cfsetospeed(&tty, B9600);
    tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
    tty.c_iflag &= ~IGNBRK;
    tty.c_lflag = 0;
    tty.c_oflag = 0;
    tty.c_cc[VMIN]  = 0;
    tty.c_cc[VTIME] = 5;
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_cflag |= (CLOCAL | CREAD);
    tty.c_cflag &= ~(PARENB | PARODD);
    tty.c_cflag &= ~CSTOPB;
    tty.c_cflag &= ~CRTSCTS;
    tcsetattr(serialPort, TCSANOW, &tty);

    // データの書き込み
    const char *data = "Hello, Serial Port!";
    if (write(serialPort, data, strlen(data)) < 0) {
        std::cerr << "データを書き込めませんでした。" << std::endl;
    }

    // データの読み込み
    char buffer[128];
    int bytesRead = read(serialPort, buffer, sizeof(buffer));
    if (bytesRead > 0) {
        std::cout << "読み取ったデータ: " << std::string(buffer, bytesRead) << std::endl;
    } else {
        std::cerr << "データを読み取れませんでした。" << std::endl;
    }

    close(serialPort);
    return 0;
}

これで、シリアルポートの基本的な読み書き方法が理解できたと思います。次に、具体的なシリアルポート操作の例を示します。

シリアルポート操作の例

ここでは、シリアルポートを使った具体的な操作例を示します。温度センサーなどの外部デバイスからデータを取得し、それをファイルに保存する例を紹介します。

シリアルポートからのデータ読み取りとファイルへの書き込み

以下の例では、シリアルポート(例:COM3)からデータを読み取り、そのデータを”sensor_data.txt”に書き込みます。温度センサーからのデータ取得を想定しています。

#include <iostream>
#include <fstream>
#include <windows.h>

int main() {
    HANDLE hSerial;
    hSerial = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "シリアルポートを開けませんでした。" << std::endl;
        return 1;
    }

    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (!GetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "現在のシリアルポート設定を取得できませんでした。" << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    dcbSerialParams.BaudRate = CBR_9600;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;

    if (!SetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "シリアルポート設定を適用できませんでした。" << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    std::ofstream outFile("sensor_data.txt");
    if (!outFile.is_open()) {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    char buffer[128];
    DWORD bytesRead;
    while (true) {
        if (ReadFile(hSerial, buffer, sizeof(buffer), &bytesRead, NULL)) {
            if (bytesRead > 0) {
                std::string data(buffer, bytesRead);
                outFile << data << std::endl;
                std::cout << "読み取ったデータ: " << data << std::endl;
            }
        } else {
            std::cerr << "データを読み取れませんでした。" << std::endl;
            break;
        }
    }

    outFile.close();
    CloseHandle(hSerial);
    return 0;
}

コードの詳細説明

このコードは以下の手順で動作します。

シリアルポートを開く

hSerial = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

この行で、COM3ポートを開きます。

シリアルポートの設定

dcbSerialParams.BaudRate = CBR_9600;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;

ここでは、ボーレートを9600bps、データビットを8、ストップビットを1、パリティをなしに設定しています。

データの読み取り

if (ReadFile(hSerial, buffer, sizeof(buffer), &bytesRead, NULL)) {
    if (bytesRead > 0) {
        std::string data(buffer, bytesRead);
        outFile << data << std::endl;
        std::cout << "読み取ったデータ: " << data << std::endl;
    }
}

この部分では、シリアルポートからデータを読み取り、ファイルに書き込みます。読み取ったデータはコンソールにも出力されます。

ファイルを閉じる

outFile.close();
CloseHandle(hSerial);

最後に、ファイルとシリアルポートを閉じてリソースを解放します。

この例を実行することで、シリアルポートの具体的な操作方法が理解できるでしょう。次に、シリアルポートを使った応用例を紹介します。

応用例:センサーデータの取得

シリアルポートを使ってセンサーデータを取得する応用例を紹介します。この例では、温度センサーからデータを取得し、そのデータをリアルタイムで表示するとともに、ファイルに保存する方法を示します。

温度センサーからのデータ取得

以下のコード例では、シリアルポートを通じて温度センサーからデータを取得し、コンソールに表示しながらファイルに保存します。温度センサーが定期的に温度データをシリアルポートに送信していると仮定します。

#include <iostream>
#include <fstream>
#include <windows.h>

int main() {
    HANDLE hSerial;
    hSerial = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if (hSerial == INVALID_HANDLE_VALUE) {
        std::cerr << "シリアルポートを開けませんでした。" << std::endl;
        return 1;
    }

    DCB dcbSerialParams = { 0 };
    dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
    if (!GetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "現在のシリアルポート設定を取得できませんでした。" << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    dcbSerialParams.BaudRate = CBR_9600;
    dcbSerialParams.ByteSize = 8;
    dcbSerialParams.StopBits = ONESTOPBIT;
    dcbSerialParams.Parity = NOPARITY;

    if (!SetCommState(hSerial, &dcbSerialParams)) {
        std::cerr << "シリアルポート設定を適用できませんでした。" << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    std::ofstream outFile("sensor_data.txt");
    if (!outFile.is_open()) {
        std::cerr << "ファイルを開けませんでした。" << std::endl;
        CloseHandle(hSerial);
        return 1;
    }

    char buffer[128];
    DWORD bytesRead;
    while (true) {
        if (ReadFile(hSerial, buffer, sizeof(buffer), &bytesRead, NULL)) {
            if (bytesRead > 0) {
                std::string data(buffer, bytesRead);
                outFile << data << std::endl;
                std::cout << "読み取ったデータ: " << data << std::endl;
            }
        } else {
            std::cerr << "データを読み取れませんでした。" << std::endl;
            break;
        }
    }

    outFile.close();
    CloseHandle(hSerial);
    return 0;
}

コードの詳細説明

このコードは以下の手順で動作します。

シリアルポートを開く

hSerial = CreateFile("COM3", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

この行で、COM3ポートを開きます。

シリアルポートの設定

dcbSerialParams.BaudRate = CBR_9600;
dcbSerialParams.ByteSize = 8;
dcbSerialParams.StopBits = ONESTOPBIT;
dcbSerialParams.Parity = NOPARITY;

ここでは、ボーレートを9600bps、データビットを8、ストップビットを1、パリティをなしに設定しています。

データの読み取り

if (ReadFile(hSerial, buffer, sizeof(buffer), &bytesRead, NULL)) {
    if (bytesRead > 0) {
        std::string data(buffer, bytesRead);
        outFile << data << std::endl;
        std::cout << "読み取ったデータ: " << data << std::endl;
    }
}

この部分では、シリアルポートからデータを読み取り、ファイルに書き込みます。読み取ったデータはコンソールにも出力されます。

ファイルを閉じる

outFile.close();
CloseHandle(hSerial);

最後に、ファイルとシリアルポートを閉じてリソースを解放します。

この応用例を実行することで、シリアルポートを使用して外部デバイスからデータを取得し、それをリアルタイムで処理する方法を理解できるでしょう。次に、理解を深めるための演習問題を紹介します。

演習問題:ファイル入出力とシリアルポート

理解を深めるために、以下の演習問題に取り組んでみてください。これらの問題を通じて、ファイル入出力とシリアルポートの操作に関するスキルを実践的に向上させることができます。

演習問題1:ファイル入出力の練習

問題1.1:テキストファイルの作成と書き込み

C++を使用して、新しいテキストファイル”myfile.txt”を作成し、その中に以下の内容を書き込むプログラムを作成してください。

Hello, World!
This is a test file.

問題1.2:テキストファイルの読み取り

前の問題で作成した”myfile.txt”を読み取り、その内容をコンソールに表示するプログラムを作成してください。

演習問題2:シリアルポートの操作練習

問題2.1:シリアルポートの基本設定

シリアルポート(例:COM3)の基本設定を行い、ボーレートを9600bps、データビットを8、ストップビットを1、パリティをなしに設定するプログラムを作成してください。

問題2.2:シリアルポートからのデータ読み取り

シリアルポート(例:COM3)からデータを読み取り、そのデータをコンソールに表示するプログラムを作成してください。読み取ったデータは、適切な形式で表示されるようにしてください。

演習問題3:応用問題

問題3.1:センサーデータの取得と保存

温度センサーがシリアルポート(例:COM3)を通じてデータを送信していると仮定します。センサーからのデータをリアルタイムで取得し、そのデータを”temperature_data.txt”に保存するとともに、コンソールに表示するプログラムを作成してください。

問題3.2:ファイルとシリアルポートの統合

センサーからのデータを一定間隔で取得し、そのデータを一時ファイルに保存します。一定のデータ量が収集されたら、そのデータをまとめて一つのファイルに保存するプログラムを作成してください。このプログラムは、データのバッファリングと効率的な保存方法を学ぶのに役立ちます。

これらの演習問題に取り組むことで、C++のファイル入出力とシリアルポートの操作に関する理解を深めることができるでしょう。次に、この記事のまとめを行います。

まとめ

本記事では、C++におけるファイル入出力とシリアルポートの操作方法について解説しました。ファイル入出力の基本操作から、シリアルポートの設定、データの読み書き、応用例まで、具体的なコード例を通じて理解を深めていただけたと思います。また、演習問題を通じて実践的なスキルも向上できるよう構成しました。

これらの技術を習得することで、C++を用いたデータ処理や外部デバイスとの通信がスムーズに行えるようになります。ぜひ、この記事を参考に、実際のプロジェクトや学習に役立ててください。

コメント

コメントする

目次