タブ区切りとは
あまり知られていませんが、Excelはタブ区切りとコンマ区切りという2つのテキスト形式を読み書きできます。
よく知られているのはコンマ区切り形式です
Name,Value,Date
"A","2,000","2000/01/01"
しかしコンマは金額でも使うものです。このためエスケープとしてダブルクォーテーションで囲む必要がありました。
Tsvは誤読されない
タブ文字(vbTab)は一般的に使われず、さらにスペースで代用できます。このため、タブ区切りは区切り文字以外には使用されていない可能性が高いため誤った区切りになる可能性が低いと言えます。
Name Value Date
"A" 2,000 2000/01/01
Qiitaではマークダウンでコード表示にすると	
が表示されます。
Name	Value	Date
"A"	2,000	2000/01/01
メモ帳(Notepad.Exe)でExcelファイルが作れる
TAB区切りで作り、UTF-16でtsvかテキストに保存すれば出来上がります。
このため、スマホやExcelがないPCでもExcelデータが作成できます。
このときコンマ付き数字が使えるという点が役立ちます。
多言語対応はできるが従来はBOM有り、BOMなしの問題があった
多言語文字を文字化けせずにCSV形式で出力するには? Re*Programing 2010/05/01
仕事で多言語サイト作成でハマった所を一つ。
内容は、多言語表示した表(文字)をCSVでダウンロード出力する。といった内容だったのですが。。。
(中略)
が、気の早いあなたには、先に結論を。
タブ区切りのUTF16LE(リトルエンディアン):Bom付で出力してあげればOKです。
これでcsvファイルのダブルクリックでちゃんと文字化けせずに読み込めます。
ただ、検証環境は要件からWindows + Excel2000です。
新しいEccelやMacOfficeは検証していません。
というか、検証してくださるとありがたい><
2010/05/10追記
Office 2008 for Mac:評価版でも正常に開ける事を確認しました。
しかしExcelはBOMの有無に関わらず読めるようになったためWin/MACの壁もなくなった
Win/Mac どちらの Excel でも正しく開ける Unicode な csv の出力方法
Excel for Mac 2016 のあるアップデートから UTF8-BOM が開けるようになったようです。
https://www.ka-net.org/blog/?p=7764
Office 2016の10月の機能更新によって、ExcelでUTF-8文字エンコードのCSVファイルがサポートされるようになりました(バージョン 1610(ビルド 7466.2038)以上)。
まだ CSV の文字化けで消耗してるの?(Excel で直接開いても文字化けしない CSVファイルを Python3 で作成するスマートな方法) 2017/12/09
そもそもUTF-16LEにはBOM付与は許されない
符号化スキームにはUTF-16、UTF-16BE、UTF-16LEの3種類ある。UTF-16BEは16ビット整数をビッグエンディアンで直列化する。UTF-16LEはリトルエンディアンで直列化する。UTF-16BE、UTF-16LEの場合はバイト順マーク (BOM) の付与は許されない。UTF-16の場合はBOMでエンディアンを明示するか、上層のプロトコルで指定されておらずBOMも付与しない場合はビッグエンディアンにするよう決められている[1]。
ここがUTF-16ならBOMの問題が生じるがLEであれば生じない。このためUTF-16LEにはBOMの問題は生じない。
UTF-16LEならFilesystemObjectで書ける
FilesystemObjectはUTF-16LEでTextStremを作成できます。
ExcelからExportするときもFilesystemObjectを使うとUTF-16になります。
UTF-16だとADODB.Streamを使わない
このため
コードがかんたんになり、誤りが少ない。
早い。
というメリットしか有りません。
Option Explicit
' VBScriptでファイル一覧作成 https://qiita.com/nobu-maple/items/20c675f9c8f4db55b4c0
'【VBS】Format関数の@書式を作ってみた https://qiita.com/yaju/items/8e07c8c6d9eeda316cd6
' FormatDateTIme関数 https://www.kanaya440.com/contents/script/vbs/function/string/format_datetime.html
' 引数を受け取る方法 https://bayashita.com/p/entry/show/79
' 使用方法 %USERPROFILE%\Documents\CscriptCurrentDirFileListUTF16LEN.vbsとする
' CMDを起動する。
' CurrentDirectryへ移動する
' Cscript.exe "%USERPROFILE%\Documents\CscriptCurrentDirFileListUTF16LEN.vbs /Ext:pdf /MaxMinutes:60 /MaxFilesCount:20000
' 名前付き引数 /Ext:pdf 拡張子 /MaxMinutes:60 処理時間(分) 最大24時間 /MaxFilesCount:20000 最大ファイル数(LimitFileCount = 10000 以上初期設定限界値LimitFileCount=500000)
Const ForReading = 1 'AS Long
Const ForWriting = 2 'AS Long
Const ForAppending = 8 'AS Long
Const WindowsFolder = 0 'AS Long
Const TemporaryFolder = 2 'AS Long
Const UTF16True = -1 'As Boolean vbTrue
Const vTristateFalse = 0
Const vTristateTrue =-1
Const vTristateMixed =-2
Const vTristateUseDefault =-2
Const LimitFileCount = 100, cnsMaxFilesLimit = 500000
Const cnsLimitTime = 30, cnsMaxLimitMinutes=1440 'Minutes
Const TsvFileFolder = "H:\"
'// INIT
Dim FSO
Dim TS
Dim DT , DT2
Dim strDate
Dim testFolder
Dim blResult : blResult = False
Dim CNT, intArgsCount : CNT = 0
Dim WSH : Set WSH = WSCript.CreateObject("WSCript.Shell")
Dim tgFolder
Dim ObjNamed
Dim ArgFilesLimite 'AS Double
Dim ArgMinutesLimit 'AS Long
DT = Now
Set FSO = WScript.CreateObject("Scripting.FileSystemObject")
Dim str
'Listを作るフォルダが無ければ終了
IF FSO.FolderExists(TsvFileFolder) = vbFalse Then WScript.Echo "Line0034:TsvFileFolder (" & TsvFileFolder & ") Not Exists. End" : Wscript.Quit
If Len(Hour(DT)) =1 then strDate = Replace(FormatDateTime(DT,vbShortDate),"/","",1,-1) & "0" & Replace(FormatDateTime(DT, vbLongTime),":","",1,-1) Else StrDate = Replace(FormatDateTime(DT,vbShortDate),"/","",1,-1) & Replace(FormatDateTime(DT, vbLongTime),":","",1,-1)
Set Ts = Fso.OpenTextFile(TsvFileFolder & "\FileList" & strDate & ".tsv" , ForWriting, True, vTristateTrue)
Ts.WriteLine "ファイル名" & vbTab & "作成日時" & vbTab & "更新日時" & vbTab & "サイズ" & vbTab _
& "属性" & vbTab & "拡張子" & vbTab & "ファイルタイプ" & vbTab & _
"ショートファイル名" & vbTab _
& "フォルダパス" & vbTab & "フォルダショートパス"
'// Main
str = FSO.Getfolder(".")
IF WScript.Arguments.Named.Count > 0 Then
Set objNamed = WScript.Arguments.Named
Wscript.Echo "Line:0051" & vbTab & ObjNamed.count
End IF
' 最大時間(分)制限
' cnsLimitTimeより小さければcnsLimitTime。cnsMaxLimitMinutes(初期値:24時間(1440分)より大きければ24時間
On Error Resume Next
IF IsEmpty(objNamed) =vbFalse And ObjNamed.Exists("MaxMinutes") And objNamed.Item("MaxMinutes") <> "" Then
IF cnsLimitTime >= CLng(objNamed.Item("MaxMinutes")) Then
ArgMinutesLimit= cnsLimitTime
ElseIF CLng(objNamed.Item("MaxMinutes")) >= cnsMaxLimitMinutes Then
ArgMinutesLimit= cnsMaxLimitMinutes
End IF
End IF
'最大ファイル数制限 初期値500000
IF IsEmpty(objNamed) =vbFalse And ObjNamed.Exists("MaxFilesCount") And objNamed.Item("MaxFilesCount") <> "" Then
IF ArgFilesLimite >= CLng(objNamed.Item("MaxFilesCount")) Then
ArgFilesLimite= cnsLimitTime
ElseIF CLng(objNamed.Item("MaxFilesCount")) >= cnsMaxFilesLimit Then
ArgFilesLimite= cnsMaxFilesLimit
End IF
End IF
On Error Goto 0
Wscript.Echo str : Wscript.Echo Cbool(IsEmpty(objNamed)) : Wscript.Echo vbTrue
FindFolder(Fso.Getfolder(str))
'// 結果報告
If blResult = False Then
DT2=Now
Wscript.Echo Cnt & "Files" & vbcrlf & "Time: " & cDate(DT2-DT) & vbCrlf & "End"
Else
Wscript.Echo "CNT:" & CNT & Vbcrlf & minute(DT2-DT) & "Count or TimeOver End"
End IF
'// 後処理
Ts.Close
Set Ts = Nothing
'FSO.Deletefolder(testFolder)
Set FSO = Nothing
Set WSH = Nothing
'// Sub Prosedure
Sub FindFolder(ByVal objMainFolder)
Dim objSubFolder
Dim objFile
Dim copyFile
'// フォルダがあれば再帰
Wscript.Echo "Line:0100 " & objMainFolder.Path & FSO.GetFolder(objMainFolder.Path).Attributes
For Each objSubFolder In objMainFolder.SubFolders
IF FSO.GetFolder(objSubFolder).Attributes <> 22 Then
FindFolder objSubFolder
End IF
Next
'// フォルダの中のファイル情報を表示 : 日時 サイズ ファイル名 フォルダパス
For Each objFile In objMainFolder.files
On Error Resume Next
IF IsEmpty(objNamed) =vbFalse And ObjNamed.Exists("Ext") And objNamed.Item("Ext") <> "" Then
If FSO.GetExtensionName(objFile) = objNamed.Item("Ext") Then
' --------------------------------------
Ts.WriteLine fnAddDBQuotes(objFile.Name) & vbTab & objFIle.DateCreated & vbTab & objFile.DateLastModified & vbTab _
& objFile.Size & vbTab & objFile.Attributes & vbTab & fnAddDBQuotes(FSO.GetExtensionName(objFIle)) & vbTab & fnAddDBQuotes(objFile.Type) & vbTab _
& fnAddDBQuotes(objFIle.ShortName) & vbTab & fnAddDBQuotes(objFile.ParentFolder) & vbTab & fnAddDBQuotes(objFIle.ShortPath)
If Cnt=ArgFilesLimite Then
blResult = True
Exit Sub
Else
CNT = CNT + 1
DT2 = Now
End IF
If Minute(DT2-DT) >=ArgMinutesLimit Then blResult = True : Exit sub
' --------------------------------------
End IF
ELSE
' --------------------------------------
Ts.WriteLine fnAddDBQuotes(objFile.Name) & vbTab & objFIle.DateCreated & vbTab & objFile.DateLastModified & vbTab _
& objFile.Size & vbTab & objFile.Attributes & vbTab & fnAddDBQuotes(FSO.GetExtensionName(objFIle)) & vbTab & fnAddDBQuotes(objFile.Type) & vbTab _
& fnAddDBQuotes(objFIle.ShortName) & vbTab & fnAddDBQuotes(objFile.ParentFolder) & vbTab & fnAddDBQuotes(objFIle.ShortPath)
If Cnt=ArgFilesLimite Then
blResult = True
Exit Sub
Else
CNT = CNT + 1
DT2 = Now
End IF
If Minute(DT2-DT) >=ArgMinutesLimit Then blResult = True : Exit sub
' --------------------------------------
End iF
Next
End Sub
Function fnAddDBQuotes(str)
fnAddDBQuotes = Chr(34) & cStr(str) & Chr(34)
End Function
これはVBScriptでファイル一覧作成と同じでCMD.exeでカレントフォルダを開きCscript.exe "%USERPROFILE%\Documents\CscriptCurrentDirFileListUTF16LEN.vbs /Ext:pdf /MaxMinutes:60 /MaxFilesCount:20000
とすると名前付き引数によってPDFのみ出力します。出力先はtsvfilefolderです。
なお
``Cscript.exe "%USERPROFILE%\Documents\CscriptCurrentDirFileListUTF16LEN.vbs`でも動きます。このときは拡張子の制限がありません。
元のVBSCRIPTはルートフォルダをカレントにするとシステム領域を踏んでエラーが起きます。しかしこれはそれを回避しました。
拡張子はTSVです。
これをExcelで読み込むときは拡張子をcsvに変えるかTSVファイルを関連付けしてください。