一回も披露したことのない豆知識

セルの値を、型宣言した変数に取り込むことはよくありますが、取り込む前のセルに入った値の型は何でしょうか?全部variantですか?セルの書式設定で標準、数値、日付等と設定出来ますが、これは型を決めているのでしょうか?試しにセルにアルファベットを入力した後、書式設定で強引に「数値」を選んでもエラーも警告も出ません。

どういう仕組なのですか?

A 回答 (8件)

こんばんは。



#5の回答者です。

他の方の書き込みを読んでいて、私の発言には、現象面だけを比較し、深く考えなかった軽率な内容の部分がありました。それをあわせて訂正と回答を述べさせていただくと、以下のような考え方になります。

最初に、VBAのVariant 型の範囲とはプロパティ面で、ワークシートのセルに格納される型の範囲とは違いがあるということです。一般的に、VBAで使われる、統合的構造体の変数のデータ型のことをVariant 型と呼んでいるわけです。もし、あえてワークシート上でも使うなら、本来はVariant の再定義が必要になってくるような気がします。XMLのデータ型を調べている最中に気がついたのは、「未設定」のものを、単にVariant型と言っているにすぎないと言えるのではないか、と考えたのです。

私は、いつもExcel上の仕様を考える時に、このExcelの制作側のコンセプトを想像することにしていますが、Excelを制作する時とVBをApplication用として搭載する時とでは、時代的にも、また用途も違うものがあるわけです。約10年の開きがあります。ただし、私の知っている範囲では、Visual Basic と、Excel , Word の元制作者は同一人物です。その中で別の考えを持った人たちがあったかは知りません。ただ、チームとして別々に存在していたし、考え方の差異そのものは、現代まで残ったままだと思います。

2つの似ていて非なる部分があるものが、両者をインターフェースで結び合わせているということです。Variant型であるかどうかという論議を出すよりも、あえて両者のデフォルト状態をVariant型とするなら、そのコンセプト(VBAとしてはプロパティ)は、元から違うものであると言えます。

また、コンセプトの違いの中には、ワークシート側のセルに、Null値の格納と、Empty値の格納の仕様はありません。ワークシートにも、この二語の用語は使いますが、概して、内容的に意味が違っていたりするほうが多いです。

セルとVBA側で基本的に共通しているのは、データ型ではなく、その表現方法(expression)だと思います。
BLANK
BOOLEAN
ERROR
FORMULA
NUMERIC(NUMBER)
STRING
だと思います。

また言うまでもなく、VB側のVariant型は、自然発生的なものではなく、ユーザー本意の人為的に作られた変数だと思います。それに比して、ワークシート側の、データの格納場所は、二重構造になっていて、片方は、文字か文字列で準ずるデータを格納し表象する場所であり、もう一つが、それに付随し計算する空間とが隣り合わせになって出来ているような印象を与えます。そのデータを格納する場所自体は、データ型を意識していない空間というわけです。

あえていうなら、VBAのRangeオブジェクトに対する、個々のプロパティのデータ型自体が、セルのデータ格納に反映しているのではない、ということになるのではないでしょうか?

仕組みの部分で、もう少し、根拠を出せると良いのですが、元が長い英文のpdfファイルなので、それをじっくり読んでいたら、どうも間にあいそうにもありません。
    • good
    • 0
この回答へのお礼

皆様ありがとうございます。既に私の頭はパンクしてるので、1年後位に読み直してみます。とりあえずは実用面では「variantだと思っとけばいい」位に考えておきます。

お礼日時:2015/02/26 07:39

#4です。


#4を読んみ返していたら、大事な事を忘れていたことに気が付いたので、
実験コードを追加します。

そういえば、エラー値や空白値などの重要な型を
書き忘れてたりもしますが、、、。
元々走り書きのプロットですし、
配列を書き換えれば追加で色々試せるようには書いたので、
あれをこちらで書き直したりするつもりはないのですが、
もし、Sub ReG8927594() を実行すること(暇)が出来たなら、
出力済のシートに対して以下の
 Sub VariantArr_VariantArr_Variant_Any()
を、試してみてください。(お暇な時に、、、。)
range.Value を 配列として変数に格納した時の
各要素の【型】を、ローカルウィンドウで見てみたら
少し理解が深まるのではないかと、、、。
各要素が内部形式を伴ったVariant型であることが確認できます。
(#因みに配列としての range.Value と Variant型変数との
  出し入れって、これでも結構高速だったりします。)
対して、
 Sub Variant_LongArr()
の方は、
Variantの(配列ではないスカラー)変数に格納する点は同様ですが、
中身はすべてLong型です。
Variant型スカラー変数に配列を格納したからといって、
それが理由で要素すべてがVariant型になる訳ではありません。
range.Value プロパティでセル値を取得する場合、は、
Variant 型になっちゃいますよね(.Formulaでも一緒)。
というお話なんです、、、。
(Value2やn=Cells(i, j)のような形だとちょと違うようなのですが、、、)
まぁExcel VBA のVariantは難しいですよね、そこが魅力に感じる私です。
世間ではVariant 型を使うデメリットがあるとかないとか、
色々考えさせられちゃって混乱しちゃうのかも知れませんが
ご自身で色々書いて試してする中で、実感できたものから、
Excel VBA に合ったご自分のスタイルを築いて貰えたらいいな、
と思っています。

Sub VariantArr_VariantArr_Variant_Any()
Dim v As Variant ' ※ Dim v() As Variant とは違い、要素をVariant宣言している訳ではありません。
  v = Range("B2", Cells(2, 2).End(xlToRight)).Value
  MsgBox "[Stop ステートメント]により、" & vbLf & "デバッグ(中断)モードでVBEが開きます" _
      & vbLf & vbLf & vbTab & "ローカルウィンドウを表示して""" _
      & vbLf & vbTab & "変数 v の要素について【型】を確認してください" _
      , vbInformation
  Stop ' Variant型の(スカラー)変数である v の各要素の【型】をローカルウィンドウで、、、
End Sub

Sub Variant_LongArr()
Dim v As Variant
Dim arrN(1 To 2) As Long
  arrN(1) = 99
  v = arrN()
  Stop ' Variant型の(スカラー)変数である v の各要素の【型】をローカルウィンドウで、、、
End Sub
    • good
    • 0

そもそもExcel自信がどの開発言語で書かれているかですが、


まず間違いなくVBは使われていません。
Netで調べたかぎりでは、やはりC++が使われている様です。

すると、C++の基本型にはVariant型なんぞはありません。
( VC++には、MSDNライブラリにVariant型なるクラス定義が
  ありますが、これはC++の全ての基本型をUNIONしたものです。)

さて、VBAからセルにアクセスするときは、セルはRangeオブジェクト
として取扱われています。このことから、セルはC++のRangeクラス
として定義されていることにまちがいないでしょう。

で、セルからVBAの変数に値を取り出すのには、RangeクラスのCellsやValueプロパティ
を使いますが、プロパティとは、ご存じのように、メンバデータかメンバ関数です。

したがって、VBAのIntegerやDoubleなどの変数に値を取り出すときは、
それに対応した変換関数(メンバ関数)が使われますし、Variant型はおそらく
値へのポインターとして実装されているのでしょう。

ということで、Excelのセル値の記憶形式は、VBAでのVariant型なんかではないはずです。
    • good
    • 0

#3の回答者です。



この話は、もともとは開発上の問題で、開発当初(Microsoftではありません)から、この表計算ソフトは、文字と数値を両方入れられる構造にしたと読んだことがあります。それが、今のVariant型と結びつくとは言えませんが、Lotus123との違いは、表示形式の割り込みにありますね。DateやTimeのような表示には、アプリ側が割り込んでくるわけで、数値(計算)と数字(文字)の区別というものは、本来、ありませんから、計算の際にのみ、キャストされるようですね。

私が、表計算のセル上で、Variant型と考えたのは、質問者さんとは違い、ワークシート上で使用する「ユーザー定義関数」は、出力型を、Variant型にするのが、良いのだと考えました。そうしないと、エラー値が返らないからです。エラー値は、CVErr関数を用いて返るからです。

#4様の書き込みを読んでいて、私の書いた#3の内容に、修正と説明不足がありました。
あたかも、円マーク(¥)が先頭についてものは、VBAでは、Double型であるような表現をしてしまいました。
「例えば、ワークシートのセルの書式に、円マーク(¥)が先頭についたものは、Currency型になると思ったら、残念! そのまま暗黙的なDouble型(Variant/Double)でしかありませんでした。ワークシート上の数値は、すべて、Double型に収まるようです。」

正しくは、確かに、VBA側では、Variant/Currency型と認められるのですが、ワークシート上で、Currency型では計算されていないようなのです。それで、ジャストシステムの「三四郎」を持ちだしたということです。

ちなみに、"Variant" というのは、「変異、異型」という意味で、変数は"Variable"です。
両方とも、数学英語のようで、あまり使ったことがありません。
    • good
    • 0

こんにちは。



> セルの値を、型宣言した変数に取り込むことはよくありますが、取り込む前のセルに入った値の型は何でしょうか?
"セルの値を、型宣言した変数に取り込むこと" 私は、殆どやらない(殆どVariant型)です。
やるとすれば、range.Value2 プロパティ(Valueではないです)から数値を取得する場合か、
n = Cells(i, j) みたいに配列を扱う場合で取得するセルのデータ型が確実に指定されている場合、
ぐらいで、あとはほぼVariant型です。
 【Excel】 【Excel VBA】
 数値    Variant/Double
 文字列   Variant/String
 日付・時刻 Variant/Date
 通貨    Variant/Currency
などに分けられますが、一時的にはすべてVariant型で受けることになります。
"取り込む前のセルに入った値の型"については、下記のマクロでも試してみてください。

> セルの書式設定で標準、数値、日付等と設定出来ますが、これは型を決めているのでしょうか?

> 試しにセルにアルファベットを入力した後、書式設定で強引に「数値」を選んでもエラーも警告も出ません。
> どういう仕組なのですか?

[セルの書式設定][表示形式]
これは、セルへの入力確定時に、
優先して選択されるデータ型に関する定義、を決めている ということです。
優先して選択されるデータ型に変換できない入力値はそのままになります。
 [表示形式]が何らかの数値を指定した場合
 入力確定時に、数値として読むことが出来るならば
 指定した[表示形式]に関連付けされたデータ型が選択される、ということです。
例えば、[表示形式]を[通貨]を指定しておけば、
数値として読めるものは【Excel】側ですべて通貨型で確定しますし、
【Excel VBA】から値を取得した場合も、
Variant/Currency (Variant型で内部形式がCurrency型)になります。
例外があるとすれば、[Ctrl + ;][Ctrl + :]等のように、
予めデータ型(表示方法)が指定されている入力方法ぐらいです。
[表示形式][標準]というのは、何となくVariant型に似た存在というか、
まぁポリバレントなのですが、これも、優先して選択されるデータ型に関する定義、
というのが複雑になってるってだけで理屈は一緒です。
例えば、[表示形式][標準]のセルに、住所録の番地の部分だけを入力する場合等で、
 1-13-1
これは、他に解釈しようがない文字列なので、文字列値
 1-12-1
これは、日付として読むことを優先させる決まりがあるから、日付値2001/12/1
といった具合。
もう一つは、入力確定時の文字パターンによって、
純粋にセルに表示するテキストの表示を変えるだけという例外ですが、
(これは、よく誤解されている事なのですが)
日付を伴わない[時刻値]は、時刻型?でも日付型でもなく[浮動小数点数]として
扱われます。【Excel】ワークシート上でも【Excel VBA】でセルの値を取得する時も、
注意が必要です。【Excel VBA】で取得するだけではVariant/Doubleになってしまいます。
XMLソースを見ると、ちゃんと[日付時刻型]になっているみたいなので、
場面によっては[時刻値]も[日付時刻型]として扱っているのかも知れませんけれど
今回はセルの値を取得する場合の注意点、という話にとどめます。

> 全部variantですか?

答えるの難いです。
ただ【Excel VBA】では、range.Value プロパティを取得する場合は、
どのみち少なくとも一度はVariant型を経て内部形式が決まりますから、
「セルの値=.Valueを取得・格納する時はVariant型の変数を使います」
というか、Variant型を使う方が返って型がキャストされる回数が少なく済む場合も
あるように今までずっと思っていました(出力する場合は別)。
でも、【Excel】側でセルの値が"全部variant"というようなことは
今まで考えたこと無かったです。
【Excel】は【Excel】、【Excel VBA】は【Excel VBA】なんじゃないのかな?
改めて聞かれると自信を持って答えられませんけれど。

値の取得、データ型、Variant型の扱い方、という課題だったので、
オマケのコード添えておきます。
range.Value プロパティの新しい使い方は、私もまだよく解っていません。
参考になれば、と思っているのは、
 TypeName(variant)
 VarType(variant)
 range.Value
 range.Value2
 range.Formula
 range.Text
とかですね。
直接の答になるか解りませんが、> 全部variantですか?
の意味合いによっては、これが答えかも?
 If TypeName(variant) = "String" Then
 If VarType(variant) = vbDouble Then
のような判別とか、エラー値やEmpty値の受け皿とか、
Variant型の使いこなしは【Excel VBA】ならではです。
(以下の記述では、range.NumberFormatに一切手を加えていません。
 興味と余力と暇が湧いてくるようなことがあれば
 range.NumberFormatを弄ってみて確認してみるのもありかな、と)


Sub ReG8927594() ' /Excel2007以降
Dim arrV, arrF, vMSP, vSp
Dim oRegExp As Object, colMp As Object, colMs As Object
Dim n As Long, i As Long
 Cells.Clear
 arrV = VBA.Array("xwz", 909, CLng(-2147483647), CSng(1.401298E-45), CDbl("3.141592653589793"), CCur(987), CCur(12345678901234#), Date, Time, Now, True)
 arrF = VBA.Array("文字列", "整数", "長整数", "単精度浮動小数点数", "倍精度浮動小数点数", "通貨", "通貨", "日付", "時刻", "日付時刻", "論理")
 n = UBound(arrV)
 With Range("B2").Resize(, n + 1)
  .Value = arrV
  vMSP = .Value(xlRangeValueMSPersistXML)
  vSp = .Value(xlRangeValueXMLSpreadsheet)
 End With
 Set oRegExp = CreateObject("VBScript.RegExp")  ' New VBScript_RegExp_55.RegExp
 With oRegExp
  .Global = True
  .Pattern = "<s:datatype dt:([^>]*)/"
  Set colMp = .Execute(vMSP)
  .Pattern = "<Data ss:Type=""([^>]*)"">"
  Set colMs = .Execute(vSp)
 End With
 For i = 0 To n
  Cells(3, i + 2) = Replace(colMp(i).SubMatches(0), """", "")
  Cells(4, i + 2) = colMs(i).SubMatches(0)
  Cells(5, i + 2) = TypeName(Cells(2, i + 2).Value)
  Cells(6, i + 2) = VarType(Cells(2, i + 2).Value)
  Cells(7, i + 2) = "=""" & Cells(2, i + 2).Value & """"
  Cells(8, i + 2) = "=""" & Cells(2, i + 2).Value2 & """"
  Cells(9, i + 2) = "=""" & Cells(2, i + 2).Formula & """"
  Cells(10, i + 2) = "=""" & Cells(2, i + 2).Text & """"
 Next i
 Cells(2).Resize(, n + 1).Value = arrF
 Range("A1:A10").Value = [{"入力時データ型";"セル上の値(表示値)";"ValueMSPersistXML";"ValueXMLSpreadsheet";"VBA Variant TypeName";"VBA Variant VarType";"VBA range.Value";"VBA range.Value2";"VBA range.Formula";"VBA range.Text"}]
 Columns(1).Resize(, n + 2).AutoFit
 Cells(5, 1).Resize(, n + 2).Interior.Color = vbYellow
End Sub
    • good
    • 0

こんにちは。



>セルの中身はVariant ?
はい、セルの中身は、Variant型です。おまけに、ワークシート上の関数も、Vairant型の出力です。

>標準、数値、日付等と設定出来ますが、これは型を決めているのでしょうか?
セルの書式自体は、暗黙的な型は、システム上変換されます。しかし、「明示的な型」というのは存在しないようです。私もしばらく前まで期待して、例えば、ワークシートのセルの書式に、円マーク(¥)が先頭についたものは、Currency型になると思ったら、残念! そのまま暗黙的なDouble型(Variant/Double)でしかありませんでした。ワークシート上の数値は、すべて、Double型に収まるようです。また、Variant型だからこそ、#Valueなどのエラー値が排出できるわけです。

ちなみに、ジャストシステムの「三四郎」にはオプションで、Currency型も入れられるようです。

>試しにセルにアルファベットを入力した後、書式設定で強引に「数値」を選んでもエラーも警告も出ません。
それは、システム側に、その文字/数字が演算されることもないままになっているからですね。入れ物に入れただけで、通常は、エラーは発生しません。

逆に、数字(数値)をセル上に置いて、それを、Mid関数やLeft, Right関数で操作すると、文字扱いになります。(VBAでもありますが、どうも、私は、こういう方式は苦手です。)

しかし、ワークシート上は、常に、数値・日付(時間)・文字というものは監視しているから、日付と思われるものは、日付で変換してしまうわけです。

VBA側はVBA側で、ワークシート側はワークシート側で、自動的に内部の型に割り振られた表示に変わるように暗黙的な型は決まってきます。

これを、いわゆる「自動キャスト」(型の内部変換)機能、という現象の一つですね。
一般的には、VBAでは、a = "123" + 456 としても、a には合計が入ることで、"123" が、型の変換が怒っていることを指します。

日付や時間は、ワークシート上でも、VBA上でも、「自動キャスト」が起こります。
他にも、整数型と浮動点小数型を一緒にすると、Double型にキャストされるようですが、これは、精度の高い方に、キャストされるわけですね。

逆が、Mod や \ の演算子は、浮動小数点型に対応する整数型の丸められます。
ここは、ワークシート関数のMod や VB.Net と違うところですね。

と、こんな話で良かったかしら?
    • good
    • 0
この回答へのお礼

ありがとうございます。
Variantではあるが、内部的には型の自動変換が起きている、エクセルの内部に踏み込んだ回答をありがとうございます。

お礼日時:2015/02/23 20:45

No.1です。



前回の投稿で誤記がありました。
>Valiant型

>Variant型
が正解です。

どうも失礼しました。m(_ _)m
    • good
    • 0

こんばんは!



あくまで私的見解ですが・・・

>セルの書式設定で標準、数値、日付等と設定出来ますが、これは型を決めているのでしょうか?

あくまで「書式設定」の中の「表示形式」の話です。
Excel的に「数値」とみなされるものはそのように表示することが可能だという意味と解釈しています。
シリアル値にしても0以上の数値を「○月○日」のように表示させているだけです。
表示形式の「時刻(時間)」は小数点以下の数値を時刻(時間)として表示させている。

>強引に「数値」を選んでもエラーも警告も出ません。
文字列はどのように転んでも文字列ですので、表示形式の変えようがないのでエラーにならないだけだと思います。
ただ、計算式に文字列が入っていると「#VALUE」のようなエラーになるはずです。

次に「変数の型」についてですが、VBAで
仮に整数型(長整数型)などに宣言した変数に文字列を格納しようとしても
「型が一致しません」というメッセージが表示されると思います。

すなわち変数の宣言を行う場合は間違いないようにする必要があります。
どの型に宣言しようか?と迷った場合は逃げ道として
どんな型でも対応できる「Valiant型」にしておけば、とりあえずはエラーは出ない。といった感じでしょうか。
(Array関数のように変数によっては必ず「Valiant型」にしなければならないものもあります)

※ 直接の回答になっていないかもしれませんし、内容に間違いがあるかもしれませんが
とりあえずはこの程度で・・・m(_ _)m
    • good
    • 0
この回答へのお礼

ありがとうございます。
書式設定の名前の通り、書式だけ変更だったのですね。

お礼日時:2015/02/23 07:21

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!