在Excel中應用VBA批量導入數據

Excel中應用VBA批量導入數據

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

 

馬維峯

 

1.       問題由來

當一個漂亮MM向你請教如何錄製並修改一個宏,把她每次的實驗數據(幾十個數據文件)導入Excel時,你感慨道:“很多Excel專家會錄製一個宏來解決問題,然後每次使用的時候修改代碼並粘貼到需要的地方,對於一個合格的程序員,這是最要命的事情。”

漂亮MM打斷並告訴你,她不是程序員,也不想做程序員,然後命令你開始工作。

2.       通過錄制宏導入數據

對於這個無法拒絕的MM,你只好垂頭喪氣的開始面對要解決的問題,想着MM幾年<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />後博士畢業,年薪至少5萬,幹個35年,年薪10萬,還有項目提成,平時吃飯、打車、買可樂都可以報銷,當然不會像你放棄了自己的專業,做了一個爲生計奔波的程序員。

數據文件是儀器生成一系列文本格式的數據文件,格式完全一樣,目的是要把每個數據文件導入到Excel中作爲一條記錄,也就是一行。那麼,你想,可以用VBA寫一個程序,然後定位到需要的位置,讀入需要的信息就可以了[]。你打開Excel,打開VBA編輯器,準備開始寫代碼。

“開始錄宏吧”,MM提醒了心不在焉的你,你沉默了0.1秒,默唸了一下VBA的信條:“萬不得已不要寫代碼,儘量使用Office的功能”。於是你啓動Excel打開這個文本文件,按照彈出的文本文件導入嚮導對話框的步驟,使用固定列寬導入了需要的數據。數據包括2部分,第一部分是文件頭,包括一些數據信息,後面是按行放置的數據,包括結果和誤差,MM要的是後邊的數據,要把每行的數據和誤差放置到相鄰的兩列(見下圖)。

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><?xml:namespace prefix = w ns = "urn:schemas-microsoft-com:office:word" />

1數據文件部分和需要在Excel中的結果數據

 

明白了問題,一切就好辦了,打開Excel,然後開始錄製宏:首先打開文件,通過導入文本文件嚮導,讀入數據,將特定單元格的數據拷貝到一個目標Excel文件中,然後關閉這個文本文件,停止錄製宏。

錄製的宏很長,大概包括2部分。第一部分是一句打開文件,格式轉換的操作,後邊一部分是激活不同的文件,拷貝和粘貼不同的Range。你刪除掉剛纔拷貝進來的數據,運行了一下這個宏,很好,需要的數據進來了。

3.       修改宏導入成批數據

MM提醒你,這個她也能做到,但怎麼樣把所有的數據文件都導入進來。你看看她帶來的文件,文件名是“r20041124001357.txt”、“r20041124001358.txt”、“r20041124001359.txt”、“r20041124001360.txt”之類,大概是時間加序列吧。

你想,嗯,寫一個循環就可以了,你打開了剛纔錄製的宏,檢查了一下MM帶來的文件,文件名最後2位從4689,你可以寫一個i144的循環,把讀入文件部分的文件名改爲:

"r200411240013" & ( i + 45 ) & ".txt"

把粘貼目的地(range)表示行數的數字用i替換。

OK,你按下了執行按鈕,每次關閉文件的時候,有一個討厭的是否保存文件的對話框跳出來,其他好像一切正常,還好,點擊了44次鼠標後[]MM得到了需要的數據。

4.       修改VBA代碼實現一個可通用的宏

4.1.    指定要導入的文件

MM向你請教如何更改循環以導入不同的文件的數據時,你程序員的劣根性又開始衝動,你想通過一個打開文件對話框來指定需要的文件。你覺得實現應該不復雜,通過一個打開文件對話框,選擇一系列文件,然後將文件全路徑存入一個集合或數組,然後循環讀出這些文件就可以了。

你先創建了一個窗體,然後放置了一個按鈕,將CommonDialog控件引入工程,添加到窗體,在按鈕的點擊事件里加入如下代碼:

 

    Dim strFiles As String, i As Long

   

    With CommonDialog1

        .Flags = &H200& Or &H80000    '可以選擇多個文件

        .ShowOpen

       

        If .FileName <> "" Then

            strFiles = .FileName

        End If

    End With

 

    '分割返回值,返回值爲以ASCII碼爲0的分割的字符串

       '字符串第一個爲路徑,之後爲單個文件名

    Files = Split(strFiles, Chr(0))             

    For i = 1 To UBound(Files) Step 1

        Files(i) = Files(0) & "/" & Files(i)              '連接路徑和文件名,組成文件數組

    Next i

 

代碼不多,最後的文件列表保存在Files數組裏。因爲第一次使用CommonDialog控件打開多個文件,查找出多個文件的分割符號是ASCII碼爲0的字符費了你不少時間。你開始查了文檔,沒有得到信息;將FileName屬性用Msgbox輸出只有路徑,在調試狀態跟蹤時是一個怪字符分割的;你開始想想應該是Tab或者回車之類的,然後使用這些字符用Split函數分割,沒有成功;只好測試了,你將所有字符使用ASC函數輸出,發現原來是ASCII碼爲0的字符。你想,微軟的文檔向來不錯,爲什麼這個在幫助裏沒有呢?

後面的部分就簡單了。

 

For i = 1 To UBound(Files) Step 1

    strFilename = Files(i)

       DoImport strFilename

Next i

 

把原來的宏修改後保存在DoImport這個過程裏,傳入文件名即可導入這個文件,循環導入所有文件就可以了。雖然程序功能複雜了,但代碼似乎要有條理了。

4.2.    指定要導入的位置

聰明絕頂的MM很高興,馬上又舉一反三,提出應該可以指定從第幾行開始導入。你腦子轉了一下,認爲這個需求屬於合理需求[],不能不予理會。

給窗體加一個RefEdit,點擊開始的區域後返回的將是一個引用位置的字符串,使用Range函數得到該區域的引用對象(Range對象),然後就可以得到其開始行數:

Range(Me.RefEdit1.Value).Row

重構一下DoImport這個過程,增加一個mRow參數,將導入的數據全部寫到第mRow行。上面的調用過程就變成了:

 

dim mRow as long

mRow = Range(Me.RefEdit1.Value).Row

For i = 1 To UBound(Files) Step 1

    strFilename = Files(i)

       DoImport strFilename, mRow

       mRow = mRow + 1

Next i

 

你終於鬆了一口氣,臉上又浮現出了賊賊的笑容。MM也答應要請你吃飯,不過你知道兌現的可能性不大,最後還可能是MM請客你掏錢,不過不要緊,程序員的最起碼的風度和尊嚴還是要維護的。

4.3.    修改導入規則

你忽然覺得靈感一現,甚至想做一個可以導入各種格式文件的通用模塊,然後作爲加載宏發佈,於是很多實驗室都開始用你的程序,你開始狂收註冊費,Gates也坐不住了,要把你的程序買了OEMExcel……

這時,呆在一旁的MM敲了一下你的頭,把手舞足蹈的你拉回了現實。

5.       總結

晚上,你打開了日誌,寫道“替Girl friend MM解決問題一個”。

你想,今天的問題很簡單,不過,解決的問題好像很管用,這大概就是所謂的VBA之道吧,以前似乎見過一個VB之道的帖子,你決定回頭也寫一個VBA之道。那麼,第一條應該是:

錄製宏,但要修改它!

 

 

2004-11-24 凌晨)



[] 這是很多程序員的通病,喜歡從輪子造起,你也一樣。

[] 你知道有一個Sendkeys語句,可以模擬鍵盤操作關閉這個對話框,不過解決主要問題先。

[] 你認爲合理需求應該滿足以下兩條定律:第一是可以實現,第二是客戶提出的。

發佈了27 篇原創文章 · 獲贊 2 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章