VBA小模板:如何把 txt / json /xml 文件内容读入到excel 表里

0 分析问题

0.1 解决问题前,需要先分析问题

我本来是翻看我自己之前总结的一些文件处理笔记,也搜了下别人的,发现问题很多

  • 网上各种例子,也都是为了解决某一个问题/ 一类问题 的特定方案,普适性不强
  • 自己以前就是想到哪,写到哪儿,解决的都是当时的问题,没有思考其他的可能性
  • 自己以前写的挺乱,需要整理
  • 目的改进后,想从问题出发,分析好问题,明确每个分支的问题的解决办法,尽量给自己以后遇到同类问题,都可以有启发,甚至可以直接用
  • 同时,如果能方便到其他同学,那更好

0.2 所以,我决定还是需要先坐下来梳理下问题

下面这些问题,看着很蠢,实际上非常重要

  1. 首先,你要读什么?
  2. 从哪儿读,文件在 windows里是存在硬盘上的,是存在不同文件夹内的
  3. 读到哪儿?读入到1个sheet 还是多个sheet?
  4. 怎么读? 整体读入?还是分行读? 

0.2.1 第1步--你要读什么?

  • EXCEL 是处理数据的,VBA一般是辅助EXCEL的,所以我们一般写VBA目的也是处理数据文件
  • 不同的数据文件格式
  1. txt
  2. json
  3. xml
  4. 等等
  5. 甚至没有后缀名的数据文件,vba 可以读到就无所谓其后缀名

    我发现只要是文本文件,无论是.txt .xml .json 甚至没有后缀名可以用写字板等打开的文件,VBA识别起来都很轻松。

附:文本文件格式知识

  • 很多文本文件内容,可以读到,但是显示乱码
  • 需要把文件,另存为,ansi--也就是asci码的方式才可以正常显示!
  • 如果默认的是utf-8, 必须先修改,另存为重新保存下

0.2.2 第2步--从哪儿读? 也就是文件在哪儿

文件在哪儿?

  • windows的文件夹路径 / 文件路径,path的路径要设定正确,并且最好写成可配置的而不是写死路径,方便不同的本地环境使用。否则换个电脑就很容易报错。
  • 存在哪个文件夹内
  1. 是从单一文件夹内
  2. 还是从多层文件夹和子文件夹内读?     这个难度要高,需要遍历多层子文件夹

0.2.3 第3步--读到哪儿?

读到哪儿?肯定是读到excel里,但这绝对不是一个蠢问题

  • 但是是读到一个sheet表
  • 还是读到多个sheet表
  • 处理很不相同

0.2.4 第4步--读到后怎么处理?

  • 原数据是怎样的?
  1. 是分行的
  2. 还是没分行的
  3. 涉及后续的处理步骤不同,分行符用的什么?等等问题
  • 要读入后怎么处理? 
  1. 整体读入
  2. 分行读入
  3. 特殊处理,求和等等,分行处理等等

1 第1步,如何从文件夹里取到文件名字

1.1 代码例子和运行结果

  • 下面代码
  • 是从1个 绝对路径文件夹内,取所有一层内的文件名
  • 注意:测试  while 循环内的 fn=dir 修改为 fn=dir(fn) 时,因为会重复定位为第1个文件,会造成无限循环!
Sub merge2()
    fp = "C:\Users\Administrator\Desktop\k\*.*"
    fn = Dir(fp)
    Do While fn <> ""
        Debug.Print fn
        'fn=dir 达到了显示下一个dir里下一个文件名的效果
        fn = Dir        '第1次后就可以不带参数乐,就可以直接输入 dir显示目录里的后面的文件名
'        fn = Dir(fp)  ' 如果这么些,会重新查询dir,且输出dir里的第1个文件的文件名,会造成无限循环!
    Loop

End Sub

 

1.2  dir() 函数

1.2.1 基础语法

  • Dir(pathname,attributes)
  • path  路径,且是绝对路径
  • attributes,属性,可以缺省。但是好像填了属性,必须填路径
  • 详细的我没查,需要的时候再查

1.2.2 dir() 函数特点

  • 在一段代码内部
  • 第1次使用,必须指定路径,得到的是目录中的第1个文件名
  • 第2次使用,可以指定路径,如果指定路径,得到的还是目录中的第1个文件名
  • 第2次使用,可以不指定路径,如果写成dir(),得到的还是目录中的第2个文件名,从而实现了dir() 逐个获得目录下每个文件名的效果

1.3  试验代码

  • 这段代码,故意测试了几种不同的写法
  • 可以看出 dir()   dir(fp) 的差别
Sub merge21()
    fp = "C:\Users\Administrator\Desktop\k\*.*"
    fn = Dir(fp)
    fn1 = Dir(fp)
    fn2 = Dir(fp)

    For i = 1 To 3 Step 1
        Debug.Print "fn2=" & fn2,
        Debug.Print "fn1=" & fn1,
        Debug.Print "fn=" & fn,
        Debug.Print
        
        fn = Dir
        fn2 = Dir(fp)
    Next
   
End Sub

1.4  遍历dir下所有文件名的小套路,判断文件名不为空即可一直遍历

  • fn 这里即代表 具体的文件名
    Do While fn <> ""
        fn = Dir       
    Loop

3 如何从文件夹内遍历取出多个子文件夹内的所有文件,这个还没搞--暂缺

因为理论上是无限的

所以这个需要写一个 while 循环遍历文件才可以

4 取某一个文件里的内容

4.1 整体取出到excel

  • 读取文件内容
  • 本身用 line input  是想逐行读入
  1. 文件本身是分行的,就会分行读入
  2. 如果文件内容没有分行,这里就会把全部内容读成1行(根据需要可能需要分行)
Sub merge3()

    Dim path1
    path1 = "C:\Users\Administrator\Desktop\k\"
    k = 1
    
    Open path1 & "101.txt" For Input As #1          '我手动专门增加了一个101的txt文件,分行的数据
    Do While Not EOF(1)
       Line Input #1, str1
       Sheets("a").Cells(k, 1) = str1                '本身就分行的数据,和整体需要再分割分行的数据
       Debug.Print str1
       k = k + 1
    Loop
    Close #1

    '如果1行,如果多行,处理方法不同?


End Sub

4.2 文本流 ----textstream对象

  • 先需要理解textstream对象这个概念

什么是textstream对象?

  • textstream对象,包括各种对象,不仅是文件,甚至可以是网页等等
  • 是 stream 是流,就是有序的,理解为一条直线也没问题
  • 比如  abcd#$%abcd#$%abcd#$%abcd#$%abcd#$%abcd#$%
  • 我是这么理解的,对于内容文件,就是把内容,按水流那样按顺序展示出来,或按顺序进行处理,甚至可以理解为 字符串流。
  • 为啥可以理解为字符串流,这些内容一般就是字符串嘛

为啥要了解textsteam对象?

  • 也就是说,文本文件,网页等都视为textstream对象
  • 那就可以把文件想成,一个字符串组成的  字符串数据流  abcd#$%abcd#$%abcd#$%abcd#$%abcd#$% 这类的,后面操作文件内容时,就能理解各种 各种语句和函数了
  • 比如 open 语句
  • 比如 eof(1) 函数

4.3  函数EOF()

4.3.1 eof() 语法

  • 括号内是,文件代号
  • 若之前定义文件为#1 ,则  eof(1) 即可,后面关闭 close #1 
  • 若之前定义文件为#2 ,则  eof(2) 即可,后面关闭 close #2 

4.3.2 网上查到的资料

  • 前面说到了,文件当成 textstream ,那么既然是有序的  字符串流,那么就需要指针,据说
  • eof ()  指针指到了,文档内容的末尾,也就是最后一个记录之后
  • bof()   指针指到了,文档内容的开头,第一个内容之前。文档开头!
  • bof()   beganing  of file ?
  • eof()   end  of  file ?
  • bof() 和 eof()  返回的都是 bool 值,true 或者 false
  • 当指针指到文件中间的时候, bof(1) =false 同时  eof(1) =false

4.4  打开文件语句,open 语句

  • 语法:Open pathname For mode [Access access] [lock] As [#]filenumber [Len=reclength]
  • 其中access、lock、reclength为可选参数,一般不用。
  • mode 指定打开文件的方式。有5种:
  1. Input:以输入方式打开,即读取方式。
  2. Output:以输出方式打开,即写入方式。
  3. Append:以追加方式打开,即添加内容到文件末尾。
  4. Binary:以二进制方式打开。
  5. Random:以随机方式打开,如果未指定方式,则以 Random 方式打开文件。(也就是缺省时,随机方式打开?有点不靠谱,为啥不默认是读取方式呢。。。)

  • Open path1 & "101.txt" For Input As #1
  • Open path1 & "101.txt" For output As #1
  • Open path1 & "101.txt" For append As #1
  • Open path1 & "101.txt" For binary As #1
  • Open path1 & "101.txt" As #1    '类random

4.4  bof() 

4.5 LOF() 函数

  • 语法:LOF(filenumber)
  • 功能:返回一个 Long,表示用 Open 语句打开的文件的大小,该大小以字节为单位。
  • 例子: lof(1) 返回文件字体大小

代码举例

  • 如果1行,101 ,lof(1) 返回3
  • 如果2行,101chr(10)102,lof(1) 返回8 (3+3+2)
  • 如果3行,101chr(10)102chr(10)103 ,lof(1) 返回13  (3+3+3+2+2)
  • 如果4行,101 chr(10)102chr(10)103chr(10)104,lof(1) 返回16 (3+3+3+3+2+2+2)
  • 如果5行,101chr(10)102chr(10)103chr(10)104chr(10)105 ,lof(1) 返回23 (3+3+3+3+3+2+2+2+2)
  • 为什么是这样
  1. 第1,lof()  返回的是字节数
  2. 第2, lof() 也会统计各种不可见字符的字节数,比如换行 chr(10)
  3. 我觉得,换行chr(10) 被统计为2个字节,ascii码里就是10


4.6 Loc() 函数


语法:LOc(filenumber)
功能:返回一个 Long,在已打开的文件中指定当前读/写位置。

4.7  关闭文件语句,close 语句

  • 语法:Close [filenumberlist]
    filenumberlist 参数为一个或多个文件号,若省略 filenumberlist,则将关闭 Open 语句打开的所有活动文件。
  • 打开文件后,必须在使用完后关闭文件。否则打开文件时会弹窗报错“文件已打开!”
  • close #1
  • close   '关闭block内所有open打开的文件,可好

4.8  Reset  语句

  • Reset 语句关闭 Open 语句打开的所有活动文件,并将文件缓冲区的所有内容写入磁盘。
  • 感觉 等同于 close 不带参数

4.9 FreeFile 函数

  • 语法:FreeFile[(rangenumber)]
  • 参数 rangenumber指定一个范围,以便返回该范围之内的下一个可用文件号。指定 0(缺省值)则返回一个介于 1 – 255 之间的文件号。指定 1 则返回一个介于 256 – 511 之间的文件号。
  • 功能:提供一个尚未使用的文件号。
  • 一般情况下,直接 open XXX for input as #1 这种直接把原来的#1给覆盖了把,所以是有点粗暴,但是好用是没问题。
  • 所以我现在能想到的应用场景就是:不想弄乱其他的文件代号# 而随便用一个文件代号,用 freefile 生成一个,然后消除。
  • 具体例子,见下面的 input 和 line input 同时使用了 freefile

4.10 读入文件 input 语句

  • 读入方式
  • input           '整体读入,而且无视 文本开头的空格!
  • line input    ’分行读入,以分行符 chr(10) 为标志,并且,这次只读1行!
  • 同时,指针会下移动,下次读下一行

 line input可直接识别line input不可识别
但是可以通过split()分行
 
 文件分多行文件分多行文件内未分行
input只能读到第1行只能读到第1行全部内容读成为1行
line input不可识别的多行全部内容读成为1行全部内容读成为1行全部内容读成为1行
line input可识别的多行读成为多行全部内容读成为1行全部内容读成为1行
line input+split()可处理的多行读成为多行读成为多行全部内容读成为1行

4.10.1 下面这2个过程,先读一个 101.txt 本身数据是多行的文件

Sub test201()

    fnum = FreeFile
    
    Open "C:\Users\Administrator\Desktop\k\101.txt" For Input As #fnum
         Input #fnum, aaa1
         Debug.Print aaa1
         Debug.Print "test201结束"
    Close fnum

End Sub


Sub test202()

    fnum = FreeFile
    
    Open "C:\Users\Administrator\Desktop\k\101.txt" For Input As #fnum
    Do While Not EOF(fnum)
         Line Input #fnum, aaa1
         Debug.Print aaa1
     Loop
         Debug.Print "test202结束"
    Close fnum

End Sub

4.10.2 下面这2个过程,修改为,读一个 102.txt 本身数据是1行的文件


Sub test201()

    fnum = FreeFile
    
    Open "C:\Users\Administrator\Desktop\k\102.txt" For Input As #fnum
         Input #fnum, aaa1
         Debug.Print aaa1
         Debug.Print "test201结束"
    Close fnum

End Sub


Sub test202()

    fnum = FreeFile
    
    Open "C:\Users\Administrator\Desktop\k\102.txt" For Input As #fnum
    Do While Not EOF(fnum)
         Line Input #fnum, aaa1
         Debug.Print aaa1
     Loop
         Debug.Print "test202结束"
    Close fnum

End Sub


  

4.10.3 如果文件需要分行呢?需要做处理

  • 有些文件内容,其实是分行的,但是无法 被line input 识别为分行内容
  • 推测原因
  1. 可能是因为文件格式问题,比如不是 ansi 而是utf-8等
  2. 有些换行符,line input 不识别
  3. 文件里有可能是用其他特殊符号分割的,比如  | ,$  等等
  • 所以先把内容,用split() 函数分割为一个数组,然后再把这个数组进行输出
  • split() 函数 ,返回一个数组对象,index从0开始
  • split(文本对象,分割符) 

代码举例,读62这个文件的内容,保存为utf-8格式

4.10.4 代码效果比较

  • test205() ,整体读入,会发现,input 只会读到第1行,无论debug.print 还是读到 excel里
  • test206() ,按行line input 逐行读入,会发现,实际上EXCEL里还是不能分行,还是读到了一行里去了,debug.print里看不出来是否分行,和原来文件内容现实一样
  • test207(),按行line input 逐行读入,切先把内容split(),然后读数组的元素,就实现了EXCEL表里的分行效果
 line input可直接识别line input不可识别
但是可以通过split()分行
 
 文件分多行文件分多行文件内未分行
input只能读到第1行只能读到第1行全部内容读成为1行
line input不可识别的多行全部内容读成为1行全部内容读成为1行全部内容读成为1行
line input可识别的多行读成为多行全部内容读成为1行全部内容读成为1行
line input+split()可处理的多行读成为多行读成为多行全部内容读成为1行

test205() 代码和跑得结果

Sub test205()

    Dim sh1 As Object
    fnum = FreeFile
    Set sh1 = ThisWorkbook.Worksheets("a")
    
    Open "C:\Users\Administrator\Desktop\k\62" For Input As #fnum
         Input #fnum, aaa1
         Debug.Print aaa1
         sh1.Cells(1, 2) = aaa1

         Debug.Print "test205结束"
    Close fnum

End Sub

test206() 代码和跑得结果

Sub test206()

    Dim sh1 As Object
    fnum = FreeFile
    Set sh1 = ThisWorkbook.Worksheets("a")
    m = 1
    
    Open "C:\Users\Administrator\Desktop\k\62" For Input As #fnum
    Do While Not EOF(fnum)
         Line Input #fnum, aaa1
         Debug.Print aaa1
         sh1.Cells(m, 2) = aaa1
         m = m + 1
     Loop
         Debug.Print "test206结束"
    Close fnum

End Sub

test207() 代码和跑得结果

Sub test207()     '尝试把文件内容已经分行,但是line input 无法直接识别的内容,变为分行后读入

    Dim sh1 As Object
    fnum = FreeFile
    Set sh1 = ThisWorkbook.Worksheets("a")
    m = 1
    Open "C:\Users\Administrator\Desktop\k\62" For Input As #fnum
    Do While Not EOF(fnum)
         Line Input #fnum, aaa1
         lll1 = Split(aaa1, Chr(10))                     'split()返回一个数组,以chr(10)为间隔分行
'         For i = 0 To UBound(lll1) Step 1               'split()返回一个数组,index从0开始,千万别写成1,会少1个数据
         For i = LBound(lll1) To UBound(lll1) Step 1
             Debug.Print lll1(i)
             sh1.Cells(m, 2) = lll1(i)
             m = m + 1
         Next
         
     Loop
         Debug.Print "test207结束"
     Close fnum
         

End Sub

5  这段代码是对数据做特殊处理的代码

  • 把分行后的数据,还做了特殊处理
  • 把不同行的数据放在了EXCEL的不同列

Sub merge13()   '只适用1个文件夹内的合并到1个excel, 而并没有遍历全部子文件夹里的文件

    Dim s As String
    Dim fp, fn
    Dim sh1 As Object
    Set sh1 = ThisWorkbook.Worksheets("sum")
    sh1.Range("A:d").Clear
    fff1 = sh1.Cells(1, 7)

    m = 1
    fp = fff1 & "*.*"
    fn = Dir(fp)
        i = 0
        Do While fn <> ""
'            path1 = "C:\Users\Administrator\Desktop\k\" & fn
            path1 = fff1 & fn
            Open path1 For Input As #1
'            If i >= 1 Then
'               j = j + maxr1
'            End If
            Do While Not EOF(1)
                Line Input #1, s1
                l1 = Split(s1, Chr(10))
                    For j = 0 To UBound(l1) Step 2
                        sh1.Cells(m, 2) = "'" & l1(j) & ""
                        sh1.Cells(m, 3) = "'" & l1(j + 1)
                        sh1.Cells(m, 4) = fn
                        m = m + 1
                    Next
            Loop
'            maxr1 = sh1.Cells(1, 1).End(xlDown).Row
            Close #1
            
            i = i + 1
            sh1.Cells(i, 1).Value = fn
            fn = Dir
        Loop



End Sub

Sub merge12()

    Dim s As String
    Dim fp, fn
    Dim sh1 As Object
    Set sh1 = ThisWorkbook.Worksheets("sum")
    sh1.Range("A:B").Clear


    m = 1
    fp = sh1.Cells(1, 3) & "*.*"
    fn = Dir(fp)
        i = 0
        Do While fn <> ""
'            path1 = "C:\Users\Administrator\Desktop\k\" & fn
            path1 = sh1.Cells(1, 3) & fn
            Open path1 For Input As #1
'            If i >= 1 Then
'               j = j + maxr1
'            End If
            Do While Not EOF(1)
                Line Input #1, s1
                l1 = Split(s1, Chr(10))
                    For j = 0 To UBound(l1)
                        sh1.Cells(m, 2) = l1(j)
                        m = m + 1
                    Next
            Loop
'            maxr1 = sh1.Cells(1, 1).End(xlDown).Row
            Close #1
            
            i = i + 1
            sh1.Cells(i, 1).Value = fn
            fn = Dir
        Loop



End Sub



6 合并,取多个文件内容 + 取文件内所有/部分内容

7 总结方法--横向方法比较

1 取文件名方法

2 遍历文件夹,子文件夹的办法

3 取文件内容的多种方法

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值