Access VBA Schema.iniを作成するマクロ

# 1. 本当は良くない
例えばリンクテーブルを持っている場合にはShema.iniを操作しないほうがいいはず。
しかし、CSVを取り込むために使用する場合はどうか。この場合のInsert IntoはAccessしていないと思われる。
したがって、Insert Intoでしかcsvを使わないというときは、Accessを起動している間にSchema.iniを書き換えても問題ないと思われる。

# 2.コード

## 2-1　前提
今回はShift-Jisの文字コードによるテキストファイルでCSVファイルが存在するとする。
中身は次のようにシンプルなもの。

```csvs
IndexNO, F01
1, 100.01
2, 3003.22
```

このファイルは`C:\hoge\`にあり`CsvfileforImport.csv`というファイル名。

## 2-2 コード

次のコードは以下のようなフローになる。
まず、Schema.iniがないか確認し、なければファイルを作成する。ここで`FSO.CreateTextFile(strFolder & "\Schema.ini")`はシンプルな記述になっていることに注意。
以前、[OfficeTanakaの記事](http://officetanaka.net/excel/vba/filesystemobject/filesystemobject06.htm)によれば、`FileSystemObject.CreateTextFile(filename[, overwrite[, unicode]])`はoverwriteを省略するとTrue、unicodeを省略するとFalse（ANSI）になると出版したことを思い出す必要がある。Schema.iniはANSIでなければエラーになる。
Schema.iniがあるか作成されたならば、次にファイルを開き、`[CsvfileforImport.csv]`という行がないか探す。
それがなければ記述されていないものとして、追記する。
また、Schema.iniの読み書きはInput# Print#を使っている。これはANSIしか扱わないためUTF-8にコードが変わることがありえないためである。また、この一連のStatementはANSIしか扱えない代わりに、テキストファイルの読み書きのスピードはfSO,Adodb.streamに比べて最速といる。Schema.iniの読み書きはできるだけ短い方が安全であると考えた。

```vb
Sub SchemaCheck()
Dim varN As Integer: varN = FreeFile()
Dim FSO As Object: Set FSO = CreateObject("Scripting.FilesystemObject")
Dim strFile As String, strFolder As String
Dim s
Dim bl As Boolean: bl = False
’ ここで本当はテーブル、クエリ、フォーム、レポートを閉じる
strFile = "Schema.ini"
strFolder = FSO.GetParentFolderName(CurrentDb.Name)
IF FSO.FileExists(strFolder & "\Schema.ini") = False then FSO.CreateTextFile(strFolder & "\Schema.ini")
Open FSO.BuildPath(strFolder, strFile) For Input As #varN
Do While Not EOF(varN)
    Line Input #varN, s
    If s = "[CsvfileforImport.csv]" Then bl = True
    Debug.Print (s)
Loop
Close #varN
If bl = False Then
Open FSO.BuildPath(strFolder, strFile) For Append As #varN
Print #varN, "[CsvfileforImport.csv]" & vbCrLf & ";テキストファイルのフィールド区切り記号に、桁やテキストの区切り記号と同じ記号は使用できません"
Print #varN, "ColNameHeader=True" & vbCrLf & "Format=CSVDelimited"
Print #varN, "MaxScanRows=25" & vbCrLf & "CharacterSet=ANSI"
Print #varN, "CurrencyDecimalSymbol=. "
Print #varN, "TextDelimeter=" & Chr(34)
Print #varN, "NumberLeadingZeros=True"
Print #varN, "Col1=""[IndexNO]"" LONG" & vbCrLf & "Col2=""[F01]"" Double"
Close #varN
End If
End Sub
```

# 3. 参考文献
## 3-1 それにしてもマイクロソフトはいつこの誤訳を直せるのか
Docs > Office VBA > リファレンス > 言語リファレンス > リファレンス > 財務諸表 
財務諸表と言っているのはStatementの意味。
## 3-2 Statement
[ファイルへのデータの書き込み](https://docs.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/writing-data-to-files)
[Open](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/open-statement)
[FreeFile 関数](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/freefile-function)
この一連のステートメントがきわめて古いことは、この関数の返り値がIntegerであることからもわかりる。実際はLONGと同じバイト数を消費するため、今の関数なら返り値はLONGで上限が決まっているということが多いためである。
[Line Input # ステートメント](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/line-inputstatement)
> 開いているシーケンシャル ファイルから 1 行を読み込み、それを文字列変数に割り当てます。
> `Line Input #ファイル番号, 変数名`

> Line Input # ステートメントは、復帰 (Chr(13))、または復帰改行 (Chr(13) + Chr(10)) のシーケンスが出現するまで 1 文字ずつファイルから読み取ります。 復帰改行のシーケンスは、文字列に追加されるのではなくスキップされます。

これも変わっている。現在なら 変数名 = Line Input(#ファイル番号)となると考えられるからである。
ここでも、現在の言語と異なっている。
これもDAOでいえばdbOpenForwardOnlyと似ている。

[Write # ステートメント](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/writestatement)
> シーケンシャル ファイルにデータを書き込みます。

[Lock、Unlock ステートメント](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/lock-unlock-statements)

>
```vb
Type Record    ' Define user-defined type. 
    ID As Integer 
    Name As String * 20 
End Type 
 
Dim MyRecord As Record, RecordNumber    ' Declare variables. 
' Open sample file for random access. 
Open "TESTFILE" For Random Shared As #1 Len = Len(MyRecord) 
RecordNumber = 4    ' Define record number. 
Lock #1, RecordNumber    ' Lock record. 
Get #1, RecordNumber, MyRecord    ' Read record. 
MyRecord.ID = 234    ' Modify record. 
MyRecord.Name = "John Smith" 
Put #1, RecordNumber, MyRecord    ' Write modified record. 
Unlock #1, RecordNumber    ' Unlock current record. 
Close #1    ' Close file. 
```

この場合はRANDOMモードで開く必要があるが、このコードは完全にファイルをデータベースのテーブルとして扱っている。
また、Type宣言を行っている。
Type宣言を明示的に行わない場合、おそらく内部でRecordSetの確立時にそれを行うため、若干遅くなると思われる。
またRandomモードとは Data Access ObjectでいうdbOpenDynaset dbOpenDynamicにあたる。そうでないシーケンシャルがdbOpenForwardOnlyにあたる。
改行コードはCrでも読み込めるがCrLFの方が読み書きできる。

## 3-4
[テキスト データ ソース ドライバーの初期化](https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/initializing-the-text-data-source-driver)
AccessのSchema.iniとSQL　ServerのSchema.iniは異なっており、特に文字コードが異なる。