「Qtの基礎 - CSVファイル」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
108行目: 108行目:
   
   
     File.close();
     File.close();
}
</syntaxhighlight>
<br><br>
== CSVファイル (RFC4180準拠) の読み込み ==
RFC4180は、CSVファイルの標準的な形式を定義しており、これに従うことで互換性の高いCSV処理が可能になる。<br>
<br>
以下の例では、RFC4180の規則に従ってCSVファイルを読み込んでいる。<br>
* ダブルクォートで囲まれたフィールド内のカンマと改行を正しく処理する。
* フィールド内の二重引用符 ("") を単一の引用符 ('') として解釈する。
* 各行の末尾の空白を除去する。
<br>
<syntaxhighlight lang="c++">
#include <QFile>
#include <QTextStream>
#include <QStringList>
#include <QDebug>
QList<QStringList> readCSV(const QString &filename)
{
    QList<QStringList> result;
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
      qDebug() << "ファイルを開けません:" << filename;
      return result;
    }
    QTextStream in(&file);
    in.setCodec("UTF-8");
    QString    line;
    QStringList fields;
    bool inQuotes = false;
    QString field;
    while (!in.atEnd()) {
      QChar ch;
      in >> ch;
      if (inQuotes) {
          if (ch == '"') {
            if (in.peek() == '"') {
                field += ch;
                in >> ch;  // 次の文字(二重引用符)を読み飛ばす
            }
            else {
                inQuotes = false;
            }
          }
          else {
            field += ch;
          }
      }
      else {
          if (ch == '"') {
            inQuotes = true;
          }
          else if (ch == ',' || ch == '\n' || in.atEnd()) {
            fields.append(field.trimmed());
            field.clear();
            if (ch == '\n' || in.atEnd()) {
                result.append(fields);
                fields.clear();
            }
          }
          else {
            field += ch;
          }
      }
    }
    file.close();
    return result;
}
</syntaxhighlight>
<br><br>
== CSVファイル (RFC4180準拠) の書き込み ==
RFC4180は、CSVファイルの標準的な形式を定義しており、これに従うことで互換性の高いCSV処理が可能になる。<br>
<br>
以下の例では、RFC4180の規則に従ってCSVファイルを書き込んでいる。<br>
* カンマ、引用符、改行を含むフィールドを適切にダブルクォートで囲む。
* フィールド内の引用符を二重引用符 ("") にエスケープする。
* 各行の終わりに改行を挿入する。
<br>
<syntaxhighlight lang="c++">
#include <QFile>
#include <QTextStream>
#include <QStringList>
#include <QDebug>
bool writeCSV(const QString &filename, const QList<QStringList> &data)
{
    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
      qDebug() << "ファイルを開けません:" << filename;
      return false;
    }
    QTextStream out(&file);
    out.setCodec("UTF-8");
    for (const QStringList &row : data) {
      for (int i = 0; i < row.size(); ++i) {
          QString field = row[i];
          bool needQuotes = field.contains(QRegExp("[,\"\n\r]"));
          if (needQuotes) {
            out << '"' << field.replace("\"", "\"\"") << '"';
          }
          else {
            out << field;
          }
          if (i < row.size() - 1) {
            out << ",";
          }
      }
      out << "\n";
    }
    file.close();
    return true;
  }
  }
  </syntaxhighlight>
  </syntaxhighlight>

2024年9月7日 (土) 13:38時点における版

概要

CSV (Comma-Separated Values) ファイルは、テキストベースのデータ形式である。
各行がデータレコードを表しており、フィールドはカンマで区切られている。

Qtでは、CSVファイルの読み書きを簡単に行うことができる。

主にQFileクラス、QTextStreamクラス、QStringListを使用してCSVファイルを操作する。
QFileクラスはファイルのオープン・クローズを行い、QTextStreamクラスはテキストの読み書きを行う。
QStringListクラスは、CSVの各行を分割して扱う場合に便利である。

ファイルの読み込み時は、QFileオブジェクトでファイルを開き、QTextStreamオブジェクトを使用して1行ずつ読み込む。
各レコードは、QString::splitメソッドでフィールドに分割することができる。

書き込み時も同様、QFileオブジェクトとQTextStreamオブジェクトを使用する。
データをカンマで連結して、QTextStreamクラスの<<演算子を使用して書き込む。

また、ファイルがオープンできない、書き込むことができない等の状況に対応するため、適切な例外処理やエラー処理を実装することが推奨される。

なお、CSVファイルの形式が統一されていない場合 (例: フィールド内にカンマが含まれる)、パースが複雑になることがある。
このような場合は、正規表現やより高度なパーシング技術が必要になることもある。


CSVファイル

以下の例では、CSVファイルを作成している。

処理の流れを以下に示す。

  1. 数値データを生成する。
    ここでは、sin曲線である を生成する。
  2. 数値データをテキストデータに変換する。
  3. ファイル保存ダイアログを開いて、保存するファイル名を入力する。
  4. 全テキストデータを書き込む。


 #include <QFileDialog>
 #include <QMessageBox>
 #include <QTextStream>
 #include <QFile>
 #include <vector>
 #include <math.h>
 #include <sstream>
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
 
 using namespace std;
 
 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
 {
    ui->setupUi(this);
 }
 
 MainWindow::~MainWindow()
 {
    delete ui;
 }
 
 void MainWindow::OnButtonClicked()
 {
    // Input File Name
    QString FileName = QFileDialog::getSaveFileName(this, tr("Save CSV File"), "", tr("CSV (*.csv);;All Files (*)"));
 
    if(FileName.isEmpty())
    {  // Cancel
       return;
    }
 
    // Generate CSV Data
    int iSize = 100;
    vector<double> lx(iSize, 0.0f),
                   ly(iSize, 0.0f);
 
    double dx = 2 * 3.15159 / iSize;
    for(int i = 0; i < iSize; i++)
    {
       lx[i] = i * dx;
       ly[i] = sin(vx);
    }
 
    // Convert from Double to String
    QString strFileData = "";
    //stringstream ss;
    for(int i = 0; i < iSize; i++)
    {
        strFileData += lx[i] + ", " + ly[i] + "\n";
        //ss << lx[i] << "," << ly[i] << endl;
    }
    
    // Write to File
    QFile File(FileName);

    if(!File.open(QIODevice::WriteOnly))
    { 
        QMessageBox::information(this, tr("Unable to open file"), File.errorString());
 
        return;
    }
 
    QTextStream OutStream(&File);
    // 浮動小数点をe形式にする
    //OutStream.setRealNumberNotation(QTextStream::ScientificNotation);
 
    // 浮動小数点の桁数を固定
    //OutStream.setRealNumberNotation(QTextStream::FixedNotation);
 
    // 浮動小数点の桁数を設定
    OutStream.setRealNumberPrecision(3);
 
    OutStream << strFileData;
    //OutStream << ss.str().c_str();
 
    File.close();
 }



CSVファイル (RFC4180準拠) の読み込み

RFC4180は、CSVファイルの標準的な形式を定義しており、これに従うことで互換性の高いCSV処理が可能になる。

以下の例では、RFC4180の規則に従ってCSVファイルを読み込んでいる。

  • ダブルクォートで囲まれたフィールド内のカンマと改行を正しく処理する。
  • フィールド内の二重引用符 ("") を単一の引用符 () として解釈する。
  • 各行の末尾の空白を除去する。


 #include <QFile>
 #include <QTextStream>
 #include <QStringList>
 #include <QDebug>
 
 QList<QStringList> readCSV(const QString &filename)
 {
    QList<QStringList> result;
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
       qDebug() << "ファイルを開けません:" << filename;
       return result;
    }
 
    QTextStream in(&file);
    in.setCodec("UTF-8");
 
    QString     line;
    QStringList fields;
    bool inQuotes = false;
    QString field;
 
    while (!in.atEnd()) {
       QChar ch;
       in >> ch;
 
       if (inQuotes) {
          if (ch == '"') {
             if (in.peek() == '"') {
                field += ch;
                in >> ch;  // 次の文字(二重引用符)を読み飛ばす
             }
             else {
                inQuotes = false;
             }
          }
          else {
             field += ch;
          }
       }
       else {
          if (ch == '"') {
             inQuotes = true;
          }
          else if (ch == ',' || ch == '\n' || in.atEnd()) {
             fields.append(field.trimmed());
             field.clear();
             if (ch == '\n' || in.atEnd()) {
                result.append(fields);
                fields.clear();
             }
          }
          else {
             field += ch;
          }
       }
    }
 
    file.close();
 
    return result;
 }



CSVファイル (RFC4180準拠) の書き込み

RFC4180は、CSVファイルの標準的な形式を定義しており、これに従うことで互換性の高いCSV処理が可能になる。

以下の例では、RFC4180の規則に従ってCSVファイルを書き込んでいる。

  • カンマ、引用符、改行を含むフィールドを適切にダブルクォートで囲む。
  • フィールド内の引用符を二重引用符 ("") にエスケープする。
  • 各行の終わりに改行を挿入する。


 #include <QFile>
 #include <QTextStream>
 #include <QStringList>
 #include <QDebug>
 
 bool writeCSV(const QString &filename, const QList<QStringList> &data)
 {
    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
       qDebug() << "ファイルを開けません:" << filename;
       return false;
    }
 
    QTextStream out(&file);
    out.setCodec("UTF-8");
 
    for (const QStringList &row : data) {
       for (int i = 0; i < row.size(); ++i) {
          QString field = row[i];
          bool needQuotes = field.contains(QRegExp("[,\"\n\r]"));
 
          if (needQuotes) {
             out << '"' << field.replace("\"", "\"\"") << '"';
          }
          else {
             out << field;
          }
 
          if (i < row.size() - 1) {
             out << ",";
          }
       }
       out << "\n";
    }
 
    file.close();
 
    return true;
 }