質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.62%

  • C++

    3331questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

C/C++の実行ファイルから Excelの最大行数取得

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,381

BeatStar

score 974

C/C++でやっています。

( 再投稿になります。 )

基本的にはMinGWで組んでいるのですが、C/C++からExcel操作ができたらいいなと思い、

VC++で Excel操作をするクラスオブジェクトを組んでいます。

ほとんどサイトからの拝借ですが、エクセルの最大行数の取得ができません。

Excel VBA だと

Dim MaxRow As Long
Dim MaxCol As Long

' 1番目のシートで使われているセルの最大行数
MaxRow = ThisWorkBook.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row

' 1番目のシートで使われているセルの最大列数
MaxCol = ThisWorkBook.Worksheets(nSheet).cells(1, Columns.Count).End(xlToLeft).Column

でできるアレです。

「Excelで使える最大行数・最大列数」

ではなく、

「現在なんらかの値が入っている (= 使われている ) 最大行数・最大列数」

です。

ソースコード自体は 「【技術部】[C++] C++でのExcel操作まとめ - UMYA.net」というページから拝借して

Excel::_ApplicationPtr等のようなオブジェクトは クラスのメンバ変数 として保持し、クラスにまとめました。

インターフェース IExcel クラスを定義し、これを継承して CExcel クラス を生成。

動的リンクにしたいので このままでは使用不可能なので COMのようにして

IExcel* CreateExcelInstance( void );

という関数で IExcel型として生成して、

bool WriteExcel( IExcel* pExcel, long nSheet, long nRow, long nCol, const char *data );

のような関数にオブジェクトを渡して間接的に操作する方法にしています。

/*  定義だけです。実装は上記サイト等から
   拝借したものです。
    なので、定義のみとします。
    イメージとして。 */

// 必要なヘッダファイルはすでにインクルード済みとします。

// 必要なタイプライブラリ? も すでにインポート済みとします。


// Interface
class IExcel
{
      public:
                 virtual ~IExcel(){}

                 virtual bool Open( const char *ExcelFilePath ) = 0;
                 virtual bool Write( long nSheet, long nRow, long nCol, const char *data ) = 0;
                 virtual bool Read( long nSheet, long nRow, long nCol, char *result ) = 0;
                 virtual long CountRow( long nSheet, long nCol ) = 0;
                 virtual bool Save( void ) = 0;
                 virtual bool Kill( void ) = 0;
};



// 実装クラス
class CExcel : public IExcel
{
      private:
                 bool bVisible;
      private:
                 bool bRunning;
      private:
                 Excel::_ApplicationPtr Excel;
                 Excel::WorkbooksPtr    WorkBooks; // エクセル本体用
                 Excel::_WorkbookPtr    WorkBook;  // エクセルブック用
      protected:
                 void WcharToCString( WCHAR *wstr, char *str );
                 void ToStrCell( long nRow, long nCol, char *result );
      public:
                 CExcel( bool bVisible );
                 ~CExcel();

                 bool Run( void );
                 bool Open( const char *ExcelFilePath );
                 bool Write( long nSheet, long nRow, long nCol, const char *data );
                 bool Read( long nSheet, long nRow, long nCol, char *result );
                 long CountRow( long nSheet, long nCol );
                 bool Save( void );
                 bool Kill( void );
};


// 実行ファイルから使えるようにする関数群。
// これらを使用して間接的に操作する。

FUNCTION_DEFINITION
IExcel* CreateExcelInstance( bool bVisible ){
        try{
                  return new CExcel( bVisible );
            }catch( int e ){
                  return NULL;
            }
}

FUNCTION_DEFINITION
void DestroyExcelInstance( IExcel* pExcel ){
      delete pExcel;
}

FUNCTION_DEFINITION
bool OpenExcelFile( IExcel* pExcel, const char *ExcelFilePath ){
      return pExcel->Open( ExcelFilePath );
}

FUNCTION_DEFINITION
bool KillExcel( IExcel* pExcel ){
    return pExcel->Kill();
}

FUNCTION_DEFINITION
bool WriteExcel( IExcel* pExcel, long nSheet, long nRow, long nCol, const char *data ){
      return pExcel->Write( nSheet, nRow, nCol, data );
}

FUNCTION_DEFINITION
bool ReadExcel( IExcel* pExcel, long nSheet, long nRow, long nCol, char *result ){
      return pExcel->Read( nSheet, nRow, nCol, result );
}


FUNCTION_DEFINITION
bool SaveExcelFile( IExcel* pExcel ){
      return pExcel->Save();
}

のようにしています。

たとえば、

シート1 をデータベースのように ( 疑似データベース ) して

"A11" セルから開始するとします。

( A1~ A10 ぐらいまでは タイトルや 情報等を載せるとして。 )

名前 - ふりがな - 年齢 - 住所 - 電話番号 - ... - 備考

のようにあって、

データベースのようにするので、追加されたりするので最大行数はすぐにはわからないとします。

この場合、このC/C++の実行ファイルから追加・削除等をするとするならいいですが、

Excelに直接入れるときもあるとします。

その場合、最大行数がわからないので 追加できません。

大阪太郎 - おおさかたろう - 20歳 - 大阪府(略) - ...
東京二郎 - とうきょうじろう - 21歳 - 東京都(略) - ...
京都三郎 - きょうとさぶろう - 22歳 - 京都府(略) - ...

と三件あったら、

A11 から開始しているとしているので、

大阪太郎 = A11, 東京二郎 = A12, 京都三郎 = A13

となり、

最大行数: 13

と返ってくるようにしたいのです。

( できれば最大列数も。 )

以前質問したときの回答では

// 先ほどあったクラス CExcel のメンバ関数とする
long CExcel::CountRow( long nSheet, long nCol ){
     long row = -1;

         //ワークシート内で使用されているセル範囲を取得
         Excel::RangePtr pRange = Work_sheet->UsedRange;

         //セル範囲の行数を取得
         row = pRange->Count; // 方法1

         //セル範囲の列数を取得
         // row = pRange->Count;

         row = pRange->End[Excel::xlDown]->Row; // 方法2
return row;
}


のようなやり方を教えていただきました。

しかし、自宅に帰ってやってみると

方法1 でやると戻り値が 234

方法2 をコメントアウトし、方法1 で試すと 戻り値が long max = 234

方法1 をコメントアウトし、方法2 でやると 戻り値 が long max = 11

となってしまいます。

方法2が近いと思うのですが、A11から開始し A13 までは埋まっているので 13 が返ってくるのを期待していたのですが...

期待している戻り値: 13

for文で開始行から 適当に最大値 3000 までの行を Readメンバ関数 ( CExcel::Read ) で 読み取り、
読み取れなかった場合、break でループを抜けて戻り値として返す。

というやり方を思いついたのでやってみたところ、方法1 と同じになってしまいました。

どうすれば最大行数を取得できるのでしょうか?

[環境等]
言語           : C/C++
コンパイラ     : VC++2010 Express ( DLL側 ), MinGW ( 実行ファイル側 )
Excelバージョン: Excel 2007
知りたいこと   : C/C++から Excel操作するときに 指定シートの ( なんらかのデータが書き込まれている ) セルの「最大行数」と「最大列数」の求め方

宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

UserRangeプロパティは値だけでなく、書式が設定されていても使用済みとして認識してしまうようですので、期待した結果になりません。
また、End(方向)では空白セルがあると、そこでカウントをやめてしまうようです。

なので、CurrentRegionを使ってみてはいかがでしょうか?空白行と空白列で囲まれた範囲(Rangeオブジェクト)を返すプロパティですが、この範囲が示す一番右下のセルの行番号・列番号を取得するというやり方です。

VBAで書くとこんな感じです

    Dim maxrow As Integer
    Dim maxcol As Integer
    With Range("A11").CurrentRegion
        maxrow = .Cells(.Rows.Count, .Columns.Count).Row
        maxcol = .Cells(.Rows.Count, .Columns.Count).Column
    End With

追加(2017/01/20)
C++で書く場合、COMを扱ういろいろ方法があるのはご存じだと思いますが、私が最近MFC経由でしか使っていませんでしたので、MFCで書かせていただきます。(ビルドしてないので、ちゃんと動くかわかりませんが、ニュアンスをつかんでもらえれば)。

    CVariant varOption((long)DISP_E_PARAMNOTFOUND, VT_ERROR);
    CRange range(pWorksheet.get_Range("A11", varOption));
    CRange Current(Range.get_CurrentRegion());
    CRange Rows(Current.get_Rows());
    CRange Columns(Current.get_Columns());
    int rowsCount = Rows.get_Count();
    int columnsCount = Columns.get_Count();
    CRange maxCell (Current.get_Range(rowsCount, columnsCount));
    int maxrow = maxCell.get_row();
    int maxcol = maxCell.get_Column();


本来でしたら、エラーチェックとか厳密にしないといけないのですが、ちょっと見づらくなりますので省いています。また、MFCでのラッパークラスでは、ExcelのプロパティであるCellsやRows、ColumnsはすべてRangeとして扱うので、上記のような書き方になります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/01/10 17:27

    今外出先で手元に試せるものがないので、チェックしてみます。

    キャンセル

  • 2017/01/20 13:16

    すみませんが、C/C++でのコード例を書いて頂けませんか?

    COM使い慣れていなくて推測すらできないです。

    "CurrentRegion" で検索してみても使い方までは書いていなかったので。

    お願い致します。

    キャンセル

  • 2017/01/20 18:54

    回答の方に追記します。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C++

    3331questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。