Spiga

在Excel中使用VBA来筛选数据

2004-12-01 13:55 by 马维峰, 42941 visits, 网摘, 编辑

订购信息:
Excel VBA应用开发从基础到实践 已可从第二书店订购
http://www.cnblogs.com/maweifeng/archive/2006/08/22/483790.html


《Excel与VBA程序设计》最新消息,预计9月上市 

    

1.       问题由来

早晨还没有完全醒来,你就被电话吵醒,有一个中学同学向你请教一个Excel的问题。作为一个所谓的Excel专家,你经常会受到此类骚扰。问题大概是这样的,一个很大的Excel文件,其中有些行是重复的,也就是说,有2行是完全一样的,而有些行是不重复的,现在的问题是要找出所有不重复或者重复的行,你没有听明白。你大概考虑了一下,用“VLOOKUP”查找一下,然后重新排序,应该就可以了,你需要试一下,然后告诉他怎么用,于是你告诉他,20分钟后再打电话给你。

2.       问题解决的思路

你首先打开Excel,输入一些测试数据,大概是这个样子:

 

 

其中“张三”、“李四”有2个,其他只有一个,需要把他们分出来。首先在B列输入1,然后向下填充,在C列输入“VLOOKUP(A1,$A$1:$B$7,2,FALSE)”[①],如果找到,那么返回1,如果找不到,空着就可以了。结果C列全部变成了1 ,因为查找自己肯定可以找到,那么查找的Range必须要去除本行。

你接着找了几个其他函数,“MATCH”,“INDEX”试了试,都无法办到;那么用IF函数呢,你开始试着写IF函数。先输入第4行吧,参数和引用区域回头再处理,或许Excel聪明到可以填充出你需要的引用区域。

你输入了如下的IF函数:

IF(OR(VLOOKUP(A4,A1:B3,2,FALSE),VLOOKUP(A4,A5:B7,2,FALSE)),1,0)

真够复杂的,Excel应该开一个小窗口,然后作为代码输入这样的判断逻辑,IF函数可以嵌套7层,真不知道微软的工程师怎么想的[②],你一边嘟囔一边按下了回车,结果是“#N/A”,就是“值不可用”,你知道函数 VLOOKUP如果找不到需要的值,则返回错误值 #N/A,表达式里有了这个东东,所以不管什么计算,结果都是它了。

       从工具菜单选择“错误检查”,“显示计算步骤”,证实了你的猜测,第二个VLOOKUP函数返回的错误值 #N/A传递到了最后。

 

       这时,你同学的电话来了,你告诉他需要写一段小程序,你决定还是使用直接又简单的VBA来解决问题。

3.       VBA程序

打开VBA编辑器,插入一个模块,你不假思索的敲入了以下代码:

 

Sub SelectDouble()

 

    Dim i As Long, j As Long

   

    For i = 1 To 7 Step 1

        For j = 1 To 7 Step 1

            '不比较相同的行

            If i <> j Then

                If Range("A" & i).Value = Range("A" & j).Value Then

                    Range("E" & i).Value = 1

                End If

            End If

        Next j

    Next i

           

End Sub

 

点击运行,很好,是重复的都标志了1,没有重复的空着,然后排序就可以了。你很满意你还输入了一行注释。你拨通了你同学的电话,告诉他可以了,然后他打电话给你,你把程序念给他,告诉他该改什么地方。天知道他上学时学的什么语言,反正不是Basic,你得解释Dim是什么含义。经过一番折腾,他终于在电话另一端把代码输入了计算机。作为电信员工的他可以每天24小时用电话聊天,只是可怜你的手机话费单,你叹了口气,该去洗脸刷牙了。

4.       效率

洗完脸,刷完牙,你泡好了一杯咖啡,又回到了计算机旁边,电话又来了。你以为是告诉你已经完成了的“喜讯”,听到的却是说死机了,愣了0.1秒钟,你想想应该是程序还在执行或者是死循环。你问了他大概的数据量,知道大概有9000多条记录,还好,你想。

你检查了一下代码,没有什么死循环,也许是你同学输入时有什么错误,你把循环改到1到10000,然后拿起杯子,咽了一口咖啡,往后靠了靠,等着计算结果。几分钟过去了,还是没有结束,你觉得有些奇怪,你敲了“Ctrl + Break”,暂停了程序,将鼠标放在i变量上,显示i还是24,TNND,你知道是Range函数太慢,算了,你打电话告诉你同学,大概需要几个小时才可以计算完成。你又喝了一口咖啡,自言自语道,比起手工筛选,毕竟很快了。

但不就不到1万条纪录吗,Excel的VLOOKUP等内置函数一眨眼也就计算好了啊。

4.1.      通过数组

数组要比Range函数快一些,你把程序改了一下,定义了2个数组,首先把数据全部读入第一个数组,然后对数组进行操作,对于重复的,把第二个数组的相应部分写为1,计算完成后,根据第二个数组,把结果写回去。程序代码如下:

 

Sub SelectDouble2()

 

    Dim i As Long, j As Long

    Dim max As Long

    Dim a() As String, b() As Long

   

    max = 10000

   

    ReDim a(max) As String

    ReDim b(max) As Long

   

    For i = 1 To max Step 1

        a(i) = Range("A" & i).Value

    Next i

   

    For i = 1 To max Step 1

        For j = 1 To max Step 1

            '不比较相同的行

            If i <> j Then

                If a(i) = a(j) Then

                    b(i) = 1

                End If

            End If

        Next j

    Next i

   

    For i = 1 To max Step 1

        Range("F" & i).Value = b(i)

    Next

           

End Sub

 

你执行了一下,对于10000条纪录,大概需要不到5分钟。你觉得很满意,效率提高了几个数量级,你还没有忘记设置了一个max变量,这样,代码使用时改动就会少很多。

4.2.      使用内置函数

你又想起了VLOOKUP这个函数,真是阴魂不散。是啊,为什么VLOOKUP执行这么快,当然是因为它是编译好的,不是用VBA写的[③]。你灵机一动,为什么不用这个函数呢,在VBA中,可以使用Application.函数名,调用Excel的内置函数。这样,改过的代码如下:

 

Sub SelectDouble3()

 

    Dim i As Long, j As Long, a, b

   

    For i = 2 To 9999 Step 1

        a = Application.VLookup(Range("A" & i), Range("A1:B" & (i - 1)), 2, False)

        b = Application.VLookup(Range("A" & i), Range("A" & (i + 1) & ":B1000"), 2, False)

        If IsError(a) And IsError(b) Then

            Range("G" & i).Value = 0

        End If

    Next i

           

End Sub

 

代码很短,但有一点复杂和讨厌,循环是从2到9999,因为为了防止VLOOKUP函数的Range范围失效,所以这两行需要手动处理。IsError函数来检测返回值,如果两个返回值都是错误,则此行为单一的没有重复的行,标志为0即可。程序执行速度和上面的差不多,至少你没有感觉出来差别。

4.3.      继续Hack

到这里,你还是觉得不满意,使用数组,数据量太大会内存吃紧,使用VLOOKUP函数,代码觉得很丑陋[④]。你不知道为什么想起来二分查找之类的东东,那么,查找前应该先排序,你在Excel里把数据排了序。现在的问题是需要循环2次,复杂度为N*N,如果…...,你想如果排好了序,只需要检查当前数值和下一个是否一样,如果一样,那么把当前和下一个位置标示出来,循环变量加2,跳过下一个,如果不一样,循环变量加1继续比较就可以了,代码如下:

 

Sub SelectDouble4()

 

    Dim i As Long, Max As Long

   

    Max = 10000

    i = 1

    Do

        If Range("A" & i).Value = Range("A" & (i + 1)).Value Then

            Range("I" & i).Value = 1

            Range("I" & (i + 1)).Value = 1

            i = i + 2

        Else

            i = i + 1

        End If

    Loop While i < Max

               

End Sub

 

这个程序复杂度只有N,执行速度当然是你今天写的所有程序里最快的,而且内存占用也最小。你觉得很满意,露出了贼贼的笑容。

5.       总结

你打开了日志,开始记下了今天问题的解决过程。

你想,嗯,如果只是想怎样把Range函数变快来解决问题,速度不会有本质的提高。速度提高,第一,排序才是关键,快速的查找和搜索都是要基于排好序的内容,比如二分查找,那么,为什么数据库要建索引,索引的有无对于查找速度影响很大,道理都是一样的了;第二,查找时没有回溯,对于查找过的内容直接跳过,这个和字符串的匹配算法,好像是KMP算法[⑤],思路是一样的,嗯,那么如果不是相同的内容不是2个,是多个,那么你可以使用一个循环来前溯,并且,对于不同的个数,可以标识为不同的数字。你忽然觉得自信满满,似乎要忘了已经失业半年的事实。

 

(2004-11-23 夜)



[①] 在表格或数值数组的首列查找指定的数值,并由此返回表格或数组当前行中指定列处的数值。当比较值位于数据表首列时,可以使用函数 VLOOKUP 代替函数 HLOOKUP。具体用法可以参考Excel帮助。

[②] 作为程序员的你,一直觉得IF函数之类是浪费时间和多此一举,7层的IF函数怎么看得懂?但函数代表简单,你不想因为告诉你同学要写程序解决问题而把他吓坏。

[③] 天知道微软用什么写的这些代码,也许是C,也许是C++,肯定不是Basic,也不是C#,写它时C#还没有出生呢。

[④]或许是你没有写好。

[⑤] 虽然不是科班出身,你也学过数据结构和算法的。

Add your comment

210 条回复

    评论共3页: 上一页 1 2 3 
  1. #201楼 王飞鸿[未注册用户]2006-05-21 13:08
    马老师,请问,如果CELLS(ROW,3)单元格中为空值,如何使CELLS(ROW,4)~CELLS(ROW,21)单元格中全部置空值?
      回复  引用    
  2. #202楼 王飞鸿[未注册用户]2006-05-21 14:07
    马老师,出现"编译错误,过程太大"是什么原因?怎么解决?先谢谢了.
      回复  引用    
  3. #203楼[楼主] 马维峰      2006-05-21 14:12
    @王飞鸿
    单元格置空要逐个进行。
    过程太大应该是你的过程行数超过了限制。一般来说,一个过程不要超过200行,具体请参考VBA书附录一章。
      回复  引用  查看    
  4. #204楼 王飞鸿[未注册用户]2006-05-21 14:39
    我的程序代码是超过了200行,但我还是需要用到SELECTIONCHANGE,怎么才能再建立一个同类型的?用数组引用?
      回复  引用    
  5. #205楼[楼主] 马维峰      2006-05-21 14:54
    @王飞鸿
    VBA的过程和函数代码行数限制应该是65534行:).
    200行是出于开发方面的考虑。我估计是一行的长度是否有特别长的,一行的限制为1023 个字节。
    出现这种情况,可能是有类似问题或者语法错误。
      回复  引用  查看    
  6. #206楼[楼主] 马维峰      2006-05-21 15:08
    关于VBA工程的限制,可以参考:
    http://maweifeng.cnblogs.com/archive/2006/05/21/405632.html">http://maweifeng.cnblogs.com/archive/2006/05/21/405632.html

      回复  引用  查看    
  7. #207楼 王飞鸿[未注册用户]2006-05-21 20:21
    马老师,这句话是什么意思?“如果遇到这种错误,可将特别大的过程分割成若干个较小的过程,或将模块级声明移到另一模块,来避免此类错误发生。”要怎样做才能避免错误发生?我急需要用此程序,我可不可以把我这个表格发EMAIL给你,你帮我看看?先谢谢了。对了,您出的书中有此类问题的解决方法吗?急切等待您的书上市,向您邮购可以不?
      回复  引用    
  8. #208楼[楼主] 马维峰      2006-05-21 20:44
    @王飞鸿
    你可以将Excel文件发给我。
      回复  引用  查看    
  9. #209楼 天地闲人[未注册用户]2006-05-22 21:59
    马哥,这个问题不要写代码吧,就有IF函数写个公司就行了。
    先把要查找的数据列排序,再在相邻列中的单元格写出入以下公式:
    =IF(A1=B1,“”,“***”)
    返回的值是***的就是不重复的数据了。
    不知道马哥看法如何?
      回复  引用    
  10. #210楼[楼主] 马维峰      2006-05-22 22:31
    @天地闲人
    使用IF函数也可以,但相对麻烦一些。
    使用VBA逻辑简单一些,解决的问题也多一些。

      回复  引用  查看    
  11. #211楼 王飞鸿[未注册用户]2006-05-22 22:41
    马老师,我又遇到了一个问题,请您帮我看看,谢谢。
    在EXCEL 中:
    如:我有SHEET1中插入三个模块,将代码输入后怎么一点都没有改变,但我在SHEET1中又可以,请问为什么?是不是要在THIS WORKBOOK中设置调用语句才可以用?
    VB 编程代码行数上限为65534,像我现在在做成本分析时,基本上用的就是WORKSHEET_SELECTIONCHANG和WORKSHEET_CHANGE (RANGE对象),并且在这两个过程中输入的代码空间肯定是大于64K的,我在SHEET1中又建立一个WORKSHEET_SELECTIONCHANGE,它又出现编程错误,说是二定义的,马老师,有什么办法解决吗?谢谢.

      回复  引用    
  12. #212楼[楼主] 马维峰      2006-05-23 09:09
    @王飞鸿
    建立2个WORKSHEET_SELECTIONCHANGE肯定会不行,呵呵。你可以根据逻辑建立几个过程,然后在WORKSHEET_SELECTIONCHANGE中调用。

    建议阅读一下VBA一书的VBA语言一章。
      回复  引用  查看    
  13. #213楼 jonas[未注册用户]2006-05-24 18:19
    马老师:
    我有个关于EXCEL的问题想想您请教,我有两张表,如下
    SHEET 1 SHEET2
    COLA B C A B C
    11 123 456 23 567
    23 234 567 11 456

    数据较多,数千行。想要根据表一得A,C 列和表二的A C列判断相同的就在表二B列中填入表1的B列数据。我是刚学习编程的,老大就安排了这个工作,急着处理一批数据,还请您帮忙,谢谢!
      回复  引用    
  14. #214楼 jonas[未注册用户]2006-05-24 18:20
    马老师:
    我有个关于EXCEL的问题想想您请教,我有两张表,如下
    SHEET 1
    COLA B C
    11 123 456
    23 234 567
    SHEET2
    A B C
    23 567
    11 456
    数据较多,数千行。想要根据表一得A,C 列和表二的A C列判断相同的就在表二B列中填入表1的B列数据。我是刚学习编程的,老大就安排了这个工作,急着处理一批数据,还请您帮忙,谢谢!
      回复  引用    
  15. #215楼 阿土猪[未注册用户]2006-05-24 20:32
    晕,好像只要把内容排序,然后用EXACT()函数对比前后两行就可以了吧

    或者,用countif()计算当前行在整列中重复的个数,很快的。
      回复  引用    
  16. #216楼 王飞鸿[未注册用户]2006-05-24 22:19
    马老师,我建立了一个逻辑过程,然后在WORKSHEET_SELECTIONCHANGE中调用,成功了,但速度很慢(我加了屏幕关闭刷新及开启的语句),请问是什么原因?我将EXCEL表格发过来给您了,请您给我看看。(我可能要在SHEET1 中建立10-15个逻辑过程,并且每个过程可能含有150行-200行的代码)。我现在的内存为1G的双通道,CPU为P4(2.4G),请您帮我解决这个难题,谢谢.
      回复  引用    
  17. #217楼[楼主] 马维峰      2006-05-24 22:54
    @王飞鸿
    其实关键在于Range调用的速度比较慢,可以尽量减少调用。你看看我的回复的邮件。
    如果必须要进行大量的单元格操作,你可以参考这个帖子:
    http://maweifeng.cnblogs.com/archive/2005/06/28/182483.aspx">http://maweifeng.cnblogs.com/archive/2005/06/28/182483.aspx
      回复  引用  查看    
  18. #218楼 王飞鸿[未注册用户]2006-05-24 22:58
    马老师,我收到了您的邮件,谢谢您,非常谢谢!
    但我想控制只有在第三列中输入"硬白料方铁管"才进行后面的条件,可以吗?
      回复  引用    
  19. #219楼 王飞鸿[未注册用户]2006-05-24 23:18
    马老师,您的帖子我看了,但不是很清楚(初学者嘛,请原谅),您可不可以将我发给您的EXCEL 表格用数组给我回一个?还是用我的那个FOR 循环语句,好不好?因为我想建立多个逻辑过程(我要按公司的材料单独建立子过程调用,以后如有原材料价格改变,可以很快的进行查找修改),谢谢!
      回复  引用    
  20. #220楼[楼主] 马维峰      2006-05-24 23:37
    @王飞鸿
    关于提高速度的问题,如果要操作大量的单元格,可以这样使用,例如要操作第3列到第5列(100行),可以定义一个数据
    dim var(1 to 100, 1 to 3) as variant
    读入单元格到数组:
    var = range("c1:e:100").value
    然后操作数组。
    最后读入数组到单元格:
    var = range("c1:e:100").value = value
      回复  引用  查看    
  21. #221楼 only[未注册用户]2006-05-25 10:43
    马哥,您好!
    我机器现在装了EXCEL-2003,与2000比起来虽然看起来比较舒服,功能更加人性化了一些,但不过一个最大的不方便就是反映太慢了。我现在一个表里共有6个sheet,需要相互之间调用,其中数据最多的一个 sheet里有近20000多行,现在每进行一步操作要等好长时间,已十分影响效率,对此马哥有何建议改进,不胜感激!
    顺便问一下,您在那个城市居住?北京吗
      回复  引用    
  22. #222楼 jonas[未注册用户]2006-05-25 11:41
    马老师:
    请帮帮我这个问题啊,我有两张表,如下
    SHEET 1
    COLA B C
    11 123 456
    23 234 567
    SHEET2
    A B C
    23 567
    11 456
    数据较多,数千行。想要根据表一得A,C 列和表二的A C列判断相同的就在表二B列中填入表1的B列数据。我是刚学习编程的,老大就安排了这个工作,急着处理一批数据,还请您帮忙,谢谢! 回复

      回复  引用    
  23. #223楼[楼主] 马维峰      2006-05-25 12:12
    @only
    好像没有特别的办法。
    其实对于一般操作和数据处理,2000和2003功能上没有什么差别。
      回复  引用  查看    
  24. #224楼[楼主] 马维峰      2006-05-25 12:14
    @jonas
    可以使用IF函数判断,可以参考帮助文件。

    如果使用VBA,可以参考这个帖子:
    http://maweifeng.cnblogs.com/archive/2004/12/09/74985.html">http://maweifeng.cnblogs.com/archive/2004/12/09/74985.html
      回复  引用  查看    
  25. #225楼 王飞鸿[未注册用户]2006-05-25 23:35
    马老师,您好!
    您昨天说的用数组来提高速度,我试了,但是它不能运行,我再发个EMAIL 给您,麻烦您给我编一个用数组的好不好?我急着要用。
    我的情况是这样的:我要将公司所有的原材料及原材料的单价用代码输入,大概有10-15类,并且都是在WORKSHEET_SELECTIONCHANGE中用,我要建立10-15个子过程(每个子过程中有200行代码左右),再在WORKSHEET_SELECTIONCHANGE中用FOR 循环查找符合条件的调用,您昨天回的贴子很好(不用FOR循环,用TARGET),但是VBA中的代码容量要少于64k,这样的话,我全部编进去后还是出现编译错误(过程太大),所以我还是要用SUB 子过程调用才能满足我需要的功能。所以肯请您帮我用数组写一个,好不好?谢谢,谢谢!
      回复  引用    
  26. #226楼[楼主] 马维峰      2006-05-25 23:59
    @王飞鸿
    你先发过来,我抽时间看看。
      回复  引用  查看    
  27. #227楼 jonas[未注册用户]2006-05-26 09:55
    @马维峰
    马老师,我是想用VBA编个程序解决,但是你让我参考的例子只是录制宏啊,还有我的数据位置不确定,也就是说我需要比较SHEETI.A=SHEET1.A AND SHEET1.c=SHEET2.c,然后把sheet1中相应的b 列倒入到sheet2中,好像类示vlookup功能,只不过要比较两列。在access 中,我可以用 sql 的 update功能,但是excel好像没有这个功能,该怎么编写这个程序呢?谢谢您
      回复  引用    
  28. #228楼[楼主] 马维峰      2006-05-26 10:11
    @jonas
    Excel也有SQL功能,可以参考Excel一书(Mini版)的最后一章。
    使用Excel对象模型,可以逐单元格比较,和本例类似。
      回复  引用  查看    
  29. #229楼 sunny-fine[未注册用户]2006-05-26 11:08
    马老师,
    在excel中,当我点击进入sheet 1 这个工作簿时,能自动弹出一个我设计好的窗口在该工作簿中,请问这个程序怎么编?

    excel-vb beginner
      回复  引用    
  30. #230楼[楼主] 马维峰      2006-05-26 13:02
    @sunny-fine
    使用Sheet1的Active事件。
    可以参考Excel VBA一书的“对象模型”一章有关内容。
      回复  引用  查看    
  31. #231楼 王飞鸿[未注册用户]2006-05-26 21:18
    马老师,我用TARGET在SUB 子过程中写好后,怎么在WORKSHEET_SELECTIONCHANGE调用?我怎么不能调用啊?
    如果仅在WORKSHEET_SELECTIONCHANGE中用TARGET,我的代码有3000行左右,容量还是大于64K,还是会出现编译错误,过程太大。我昨天发了一个EXCEL表给您,您收到了吗?还有,我只想控制在第三列输入时才发生后面单元格的改变,这可以做到吗?我把我编的又发给您了,请您再帮我看看,真的是麻烦您了。
    对了,马老师,这些问题在您的书中有详细讨论吗?谢谢!
      回复  引用    
  32. #232楼[楼主] 马维峰      2006-05-27 00:22
    @王飞鸿
    邮件已回复。
    有时间看看书,应该会有不少好处:)。我觉得有些问题属于概念和基本知识的问题,可以看看VBA语言一章。

      回复  引用  查看    
  33. #233楼 sunny-fine![未注册用户]2006-06-03 07:51
    马老师,您好!
    我在一本vb教程上看到说excel控件箱里有个"计时器-timer",能够被用来做简单的动画,可是我的excel控件箱里怎么没有这个控件那?
    thank u!
      回复  引用    
  34. #234楼[楼主] 马维峰      2006-06-03 11:24
    @sunny-fine!
    只能说这本书胡说了,呵呵。VB中有这个控件,VBA的Form或者工作表的控件工具箱中是没有这个控件的。

    可以使用Application对象的Timer属性。可以参考Excel VBA一书的Excel对象模型一章。
      回复  引用  查看    
  35. #235楼 sunny-fine![未注册用户]2006-06-05 07:39
    马哥:
    您好,现在我想把excel中符合条件的几行数据(比如说5行3列)从一个用户窗口中显示出来,而且行列位置不变,请问这能否实现在vba中。
    谢谢指教,祝天天开心!
      回复  引用    
  36. #236楼[楼主] 马维峰      2006-06-05 12:42
    @sunny-fine!
    可以,使用用户窗体,显示数据可以使用Grid控件(需要增加到控件工具箱),或者使用多个文本框模拟。

      回复  引用  查看    
  37. #237楼 sunny-fine![未注册用户]2006-06-05 15:58
    马哥,谢谢您的回复,不过我真的不清楚什么是grid控件,如果是很复杂的,我就选用后一个方法。
    后面的方法中,假设用户窗体中文本框的名字叫aa,我要输入"hello the worlld", 看完您的书后,我这样写了:
    sub 确定click()
    open aa for output as #1
    write #1,"hello the world"
    end sub
    可是,怎么都显示有错误,请指教!

      回复  引用    
  38. #238楼[楼主] 马维峰      2006-06-05 16:27
    @sunny-fine!
    这个方法是读文件啊?
    使用控件请参考VBA语言的窗体编程一节。
      回复  引用  查看    
  39. #239楼 uu[未注册用户]2006-06-10 12:22
    马老师,
    我想新建一个工作簿,然后给它命名,
    sub 55()
    worksheets.add
    newsheet.name="00"
    end sub
    这个第三行应该怎么表达,谢谢了!
      回复  引用    
  40. #240楼[楼主] 马维峰      2006-06-10 16:32
    @uu
    可以参考Excel VBA一书的“对象模型”一章有关内容,有示例。
    http://maweifeng.cnblogs.com/archive/2006/03/31/363176.html">http://maweifeng.cnblogs.com/archive/2006/03/31/363176.html
      回复  引用  查看    
  41. #241楼 uu[未注册用户]2006-06-12 12:55
    马老师,您好!
    vba中的并列循环结构怎么编写,您能帮我举个例子吗?
      回复  引用    
  42. #242楼[楼主] 马维峰      2006-06-12 13:41
    @uu
    什么是并列循环结构?
      回复  引用  查看    
  43. #243楼 uu[未注册用户]2006-06-12 18:05
    马老师,不好意思,这个名词是我自己创造的,这个问题我明白了现在,原来我以为两个for 循环中只能是后面的嵌在前面的,原来也可以并列使用,不过同样谢谢您,一直在看您的书学着编程。
      回复  引用    
  44. #244楼 spie@163.com[未注册用户]2006-06-14 11:47
    你的这个问题,用下面函数一下搞定了,用得着这么复杂吗?
    =COUNTIF(I3:I11,I3)

    比如查找I3在I3:I11中出现的次数,一次就是只有一个,1个以上就是有重复的了。

    希望能复杂事情简单化而不是更复杂。
      回复  引用    
  45. #245楼 uu[未注册用户]2006-06-14 17:57
    马老师,您好!
    还有一个问题要请教:
    我想在每次关闭excel(文件时弹出来一个对话框,提醒要删除一个临时用的sheet,怎么实现?
    根据您的excel一书,我已经写出了这些:
    Private Sub Workbook_BeforeClose(Cancel As Boolean)
    MsgBox "有一个临时的sheet需要删除"
    End Sub
    我已经把它放到了一个类模块里,但 withevents 怎么使用,我还是没看明白,请您指教.
    十分感谢!
      回复  引用    
  46. #246楼[楼主] 马维峰      2006-06-14 22:58
    @uu
    在ThisWorkbook模块内选择BeforeClose事件,写这个过程。
      回复  引用  查看    
  47. #247楼 sunny-fine![未注册用户]2006-06-15 09:25
    马老师,问题解决了,谢谢您的回复,祝顺利!
      回复  引用    
  48. #248楼 Poloar[未注册用户]2006-06-17 09:53
    @SUMMER
    是不是要打印票据
    搜索一下
    我在网上看到过
      回复  引用    
  49. #249楼 sunny-fine![未注册用户]2006-06-25 10:33
    马老师,您好!
    在您的帮助下,我已经顺利的完成了一个excle宏的设计,并派上了用场,发挥了很大的作用在实际工作中。
    现在我还有一个问题,就是如何保护我的vba代码,以避免别人的浏览和改动。
    再次深表感谢!
      回复  引用    
  50. #250楼[楼主] 马维峰      2006-06-26 12:56
    @sunny-fine!
    可以在VBA环境下选择 工具 - [VBAProject]选项 - 保护,
    添加密码,并选择锁定工程。
      回复  引用  查看    
  51. #251楼 sunny-fine![未注册用户]2006-06-26 16:22
    谢谢您,马老师,您教的方法就是简单、实用
      回复  引用    
  52. #252楼 张先生[未注册用户]2006-06-27 21:28
    马老师:
    你好!
    我对你在EXECEL表中筛选同情的程序非常的赞赏,而且帮了我很大的忙,现在我又出现了一个非常复杂的问题比如说下边的表格:
    D32057FN TI 2
    D72008UFNL TI 2
    D72028UFNL TI 11
    D62053FN TI 10
    D12089FN TI 10
    D14003FN TI 22
    D14006FN TI 100
    D32054FNL TI 10
    D32053FNL TI 7
    D32051FNL TI 1000
    能不能写个程序把第一列相同的型号找出来并且把第三列相同的型号的数量给相加起来。然而并不是再第四列把相同的型号打上1 ,这样对于比较大的数据库,又想把相同的给去掉,做起来就很麻烦。所以想请你帮忙写个程序,把第一列相同的找出来。并把第三列相同型号的数量累加,有必要的话我想加你QQ私聊可以吗?

    希望得到你迅速的回复
      回复  引用    
  53. #253楼 sunny--fine[未注册用户]2006-06-28 11:46
    马老师,您好!
    我编的代码中有这么一段,用来判断excel某一列中有多少条连续的非空数据,首先我把sheet 2 重新命名为“操作台”:
    for i = 1 to 1000 step 1
    if worksheets("操作台").cells(i,1)="" then
    p=i-1
    msgbox p
    exit for
    end if
    next i

    程序在运行时偶尔出现调试的窗口,说 if worksheets("操作台").cells(i,1)="" then 这行数据类型不匹配,我把它写成 if worksheets(2).cells(i,1)="" then 就可以继续运行了,请问这是怎么回事?
    麻烦您了!


      回复  引用    
  54. #254楼[楼主] 马维峰      2006-06-28 15:11
    @sunny--fine
    出现“数据类型不匹配”应该是因为某些单元格的数值类型的问题吧?使用worksheets("操作台")和worksheets(2)是没有差别的。
      回复  引用  查看    
  55. #255楼[楼主] 马维峰      2006-06-28 15:17
    @张先生
    可以使用IF函数判断,也可以参照本例编程实现。
      回复  引用  查看    
  56. #256楼 张先生[未注册用户]2006-06-28 15:55
    马老师
    你好!
    那我可不可把我的样本发给你,然后你在帮找出具体的方法可以吗?

    谢谢
      回复  引用    
  57. #257楼 dany11111[未注册用户]2006-06-30 12:09
    马老师,您好!
    我想问一下,如何判断一个表的名称是否存在?
      回复  引用    
  58. #258楼 F9[未注册用户]2006-06-30 16:33
    你也太麻烦了吧!一个简单的问题尽然让你给搞成这样,还要VBA,恐怖!
    其实你第一个方法就可以解决,最多应该五分钟就可以搞掂了!把公式换一下:
    =IF(ISNA(VLOOKUP(a1,a2:$b$10000,2,FALSE)),"0",VLOOKUP(a1,a2:$b$10000,2,FALSE)),
    拖到10000,然后筛选出“0”来删除,ok!!
      回复  引用    
  59. #259楼 analyse excel[未注册用户]2006-06-30 21:18
    马老师,您好!我想请教您怎么才能EXCEL中实现以下功能,谢谢!
    如果有一个长方形长*宽为:1000*2000,现有一个小长方形为X*Y(X,Y满足以下条件:X<1000 AND Y<2000 或X<2000 AND Y<1000)(只要X,Y满足以上条件就才可进行运算),,怎么才能算出1000*2000这个长方形最多可由多少个X*Y的小长方形组成。例如:1000*2000长方形最多可由1个1000*2000长方形组成,最多可由2个990*990长方形组成(余下部分不计,必须要能放下整个小长方形X*Y才能算一个),等等,急用,谢谢!
      回复  引用    
  60. #260楼 analyse excel[未注册用户]2006-06-30 21:24
    马老师,关于锁定EXCEL中的宏不被别人访问和修改的问题.我听同事说,在"VBA环境下选择 工具 - [VBAProject]选项 - 保护,
    添加密码,并选择锁定工程"这个办法,现在在网上有很多的解密软件可以轻松破解,有没有更好的办法?谢谢!
      回复  引用    
  61. #261楼[楼主] 马维峰      2006-07-01 12:02
    @analyse excel
    这个问题没有那么严重,密码设置长一些。
    如果不想让破解,选择把一些模块放入DLL中,在VBA中调用也是一个办法,或者设计一个COM加载宏。
      回复  引用  查看    
  62. #262楼[楼主] 马维峰      2006-07-01 14:43
    @dany11111
    可以使用类似下面的代码:
    for each w in worksheets
    if w.name = "..." then
    bFind = true
    end if
    next


      回复  引用  查看    
  63. #263楼 analyse excel[未注册用户]2006-07-02 01:10
    马老师,您好!我想请教您怎么才能EXCEL中实现以下功能,谢谢!
    如果有一个长方形长*宽为:1000*2000,现有一个小长方形为X*Y(X,Y满足以下条件:X<1000 AND Y<2000 或X<2000 AND Y<1000)(只要X,Y满足以上条件就才可进行运算),,怎么才能算出1000*2000这个长方形最多可由多少个X*Y的小长方形组成。例如:1000*2000长方形最多可由1个1000*2000长方形组成,最多可由2个990*990长方形组成(余下部分不计,必须要能放下整个小长方形X*Y才能算一个),等等,急用,谢谢!
      回复  引用    
  64. #264楼[楼主] 马维峰      2006-07-02 16:49
    @analyse excel
    你还是找本算法的书参考参考吧,这个问题应该在很多书的习题里都有。
      回复  引用  查看    
  65. #265楼 sunny-fine.[未注册用户]2006-07-02 19:51
    Sub trcl()
    b = InputBox(请输入单元格范围) 输入的内容比如是: e2:e5
    x = Left(b, 1)
    y = Val(Mid(b, 2, 1))
    Range("x&y").Activate
    end sub

    运行时对话框说方法’range‘作用于对象‘—global’时失败,请问马老师这是怎么回事?
    谢谢您!
      回复  引用    
  66. #266楼[楼主] 马维峰      2006-07-02 20:21
    @sunny-fine.
    Range(x&y).Activate, 去掉引号,因为x,y已经是字符串。

    这个问题,也建一个用户窗体,使用引用控件来得到单元格引用。
      回复  引用  查看    
  67. #267楼 dany11111[未注册用户]2006-07-03 09:21
    谢谢 马老师我的问已解决,这是您的代码写的
    For Each w In Worksheets
    If w.Name = "统计结果" Then
    Sheets("统计结果").Delete
    End If
    Next
    这是我的代码写的,不知合适否?
    On Error Resume Next
    If Sheets("统计结果").Visible Then Sheets("统计结果").Delete
      回复  引用    
  68. #268楼[楼主] 马维峰      2006-07-03 12:31
    @dany11111
    2种方法都可以。
      回复  引用  查看    
  69. #269楼 sunny-fine.[未注册用户]2006-07-03 18:22
    Sub trcl()
    b = InputBox(请输入单元格范围) 输入的内容比如是: e2:e5
    Range(" ").Activate
    end sub

    我想使输入列的下一列的同行单元格"f2"处于激活状态,在空白处怎么表达?因为输入是随机的,所以我不会了这下,劳驾您指教!
      回复  引用    
  70. #270楼[楼主] 马维峰      2006-07-03 23:21
    @sunny-fine.
    Range(b).Cells(1, 2)
      回复  引用  查看    
  71. #271楼 analyse excel[未注册用户]2006-07-04 22:30
    马老师,那个关于长方形的问题我已解决了,谢谢您!其实是个很简单的问题,以前学的都忘了,赶紧复习才行,谢谢您的指导。
      回复  引用    
  72. #272楼 fengzy[未注册用户]2006-07-05 10:01
    马老师你好!
    请教个问题:
    dim s as string,yzname as string
    dim pos1 as double
    yzname=worksheets(1).cell(i,6).value
    For k = 1 To 68
    s = Worksheets(3).Cells(1, k).Value
    pos1 = Application.WorksheetFunction.Find(s, yzname)

    If pos1 〉0Then curcol = k
    Exit For
    Next k
    s,yzname 均是中文字符串,此窜程序要求在第一行中查找是否有包含在yzname中的单元格存在
    运行时报错:“不能取得workfunction的find属性” ?
      回复  引用    
  73. #273楼[楼主] 马维峰      2006-07-06 12:43
    @fengzy
    可以使用VBA函数,例如Instr等等。
      回复  引用  查看    
  74. #274楼 晨曦[未注册用户]2006-07-06 16:00
    马老师您好:
    这个问题困绕了我一天了,还没解决,用COUNTIF能实现吧,比如,A1:A8 满足等于1的条件同时C1:C8也得满足a的条件,请问公式应该怎么写?谢谢了,希望能传到我的QQ里或者邮箱,chenxihg@126.com
      回复  引用    
  75. #275楼 晨曦[未注册用户]2006-07-06 16:07
    马老师,您好,又打扰您了,顺便把我的QQ告诉您,以便能以后更好的向您请教,谢谢了
      回复  引用    
  76. #276楼 晨曦[未注册用户]2006-07-06 16:08
    QQ346156653
      回复  引用    
  77. #277楼 sunny-fine.[未注册用户]2006-07-06 21:22
    Sub 宏2()
    Dim x As String

    x = InputBox(请输入单元格的范围:)
    Range(x).Cells(1, 2).Activate
    ActiveCell.FormulaR1C1 = "=COUNTIF(range(x),RC[-1])"

    End Sub

    马老师,上面的 "=COUNTIF(range(x),RC[-1])" 中 range(x) 怎样表达才是对其单元格范围的绝对引用那,在countif 这个公式里。
    多谢您一直以来的帮助,祝天天开心!

      回复  引用    
  78. #278楼 晨曦[未注册用户]2006-07-07 10:12
    马老师,不好意思又来打扰你了,我对EXCLE的函数应用真的是不怎么太懂,只会些简单的,更不要说"宏"了,您上面说的也许是宏,但我没看明白,我编辑了一个公式但不对,您给指导指导, 也许是我没说明白,比如工作薄里A1:A10单元格内若等于1,B1:B10同时的单元格等于a的时候,符合这两个条件的的单元格的个数,计数自动在C6单元格内显示,请问C6单元格内的函数公式应该是什么?我自己编写了一个,但不对,您给修正修正
    =countif(a1:a10,"1")-countif(b1:b10,"a")但中间连接好象不对,麻烦您了~!
    QQ346156653
      回复  引用    
  79. #279楼[楼主] 马维峰      2006-07-07 10:44
    @sunny-fine.
    使用Address可以获得绝对引用的地址。
    @晨曦
    函数使用应该有帮助,你查一下。每个公式都有相应的说明。
      回复  引用  查看    
  80. #280楼 晨曦[未注册用户]2006-07-07 11:06
    马老师您好,我找了,可是符合两个条件的确实没有,单个条件的我会弄,两个条件的就不知道了
      回复  引用    
  81. #281楼 晨曦[未注册用户]2006-07-07 11:09
    要不您加我QQ我把原文件发给您得了,这样好参考
      回复  引用    
  82. #282楼 sunny-fine [未注册用户]2006-07-08 19:39
    Sub 宏2()
    Dim x As String
    x = InputBox(请输入单元格的范围:)
    Range(x).Cells(1, 2).Activate
    ActiveCell.FormulaR1C1 = "=COUNTIF(range(x),RC[-1])"
    End Sub

    马老师,按照您给的建议我试着这样写了ActiveCell.FormulaR1C1 = "=COUNTIF(address(x),RC[-1])",可是没有搞定,我怀疑在第5行的 "=COUNTIF(range(x),RC[-1])" 中的 x 是不是不可以和第3行中的 x 进行代换,那么我要想使这个 x 随着第三行的随机输入而变化应该怎么办(且是绝对引用的形式)。
    麻烦您了!
      回复  引用    
  83. #283楼[楼主] 马维峰      2006-07-08 21:12
    @sunny-fine
    这一行:
    ActiveCell.FormulaR1C1 = "=COUNTIF(range(x),RC[-1])"
    简单一些,可以写作:
    ActiveCell.value = "=COUNTIF(x,RC[-1])"
      回复  引用  查看    
  84. #284楼 sunny-fine.[未注册用户]2006-07-09 09:59

    马老师,您好,这个方法我试了,在执行“x = InputBox(请输入单元格的范围:)”语句时我输入的是b2:b9,运行完毕后,在activecell里的公式显示的依然是 =COUNTIF(x,B2),看样子,这个 x 没有办法和前面的进行代换了。既然不能对countif公式里的x进行精确控制,我又这样写了:ActiveCell.FormulaR1C1 = "=COUNTIF(R1C[-1]:R30000C[-1],RC[-1])",行数从1到30000一般足够了在实际中,而列设定为相对引用,但不影响列的向下自动填充。
    后面的办法已在我的机器进行了测试,比较成功,thank u all the same!
      回复  引用    
  85. #285楼 bluemoodiness[未注册用户]2006-07-10 17:40
    马老师你好,我想将下面的数据中第二列中,凡是中文字相同的都筛选出来,复制到另外一个sheet.可以实现吗?请指导下,谢谢!
    2006-6-1 高低村9
    2006-6-2 仁寿山1
    2006-6-3 仁寿山2
    2006-6-4 仁寿山3
    2006-6-5 仁寿山2
    2006-6-6 塘溪2
    2006-6-7 塘溪2
    2006-6-8 高低村M9
    2006-6-9 市府1
    2006-6-10 市府2
    2006-6-11 市府3
    2006-6-12 市府2
    2006-6-13 市府1
    2006-6-14 市府2
    2006-6-15 小岛2
    2006-6-16 小岛2
    2006-6-17 芙冈3
    2006-6-18 芙冈3
    2006-6-19 立圆3
    2006-6-20 网地1
    2006-6-21 网地2
    2006-6-22 网地3
    2006-6-23 立圆3
    2006-6-24 发电厂2
    2006-6-25 网地1
    2006-6-26 东向1
    2006-6-27 网地3
    2006-6-28 东向2
    2006-6-29 网地2
    2006-6-30 东向3
      回复  引用    
  86. #286楼[楼主] 马维峰      2006-07-11 01:10
    @bluemoodiness
    这个应该可以使用公式实现。
    例如VLOOKUP。
      回复  引用  查看    
  87. #287楼 analyse excel[未注册用户]2006-07-12 22:10
    马老师,我遇到这样一个问题,请您帮我看看,谢谢!
    问题是:
    如果.CELLS(1,1)满足条件A,则.CELLS(2,1)=B;如果.CELLS(3,1)满足条件C,则.CELLS(3,1)=.CELLS(3,1)*.CELLS(2,1),但我想要只要当.CELLS(3,1)满足条件后只运行一次,可以吗?请在我发给您的表格时输入少于100的数,点击.CELLS(2,1),谢谢!您的学生.
      回复  引用    
  88. #288楼 sunny-fine[未注册用户]2006-07-13 13:07
    马老师,您好!
    还有两个问题向您请教:
    1、我现在在excel里自定义了一个工具栏,主要是用来清除单元格里的多余空格,在打开其他excel文件时,这个工具栏一样可以出现在其他工具栏的后面,但是在使用它的时候,有时会出现类似“无法打开某某文件的宏”的字样,或者能执行但同时弹出来一个附属的工作簿,不知道应该怎样解决这个问题,就是最好能使这个工具栏能对我机器里的所有excel文件普遍适用。
    2、我需要在word里实现金额的大小写的转换,我这样编了:
    sub zhuanhuan()
    dim i as integer
    i=5,556
    p=application.text(i,"[DBNUM2]G/通用格式")
    msgbox p
    end sub
    在执行时,代码的第四行有问题,我想知道应该怎么修改。
    两个问题再次麻烦您,深表感谢!
      回复  引用    
  89. #289楼[楼主] 马维峰      2006-07-14 21:54
    @sunny-fine
    第一个,设计为一个加载宏试试。
    第二个,没有怎么使用过word的VBA,(1)text的函数使用是否正确;(2)这个函数能否在VBA中使用。
      回复  引用  查看    
  90. #290楼 supperST[未注册用户]2006-07-15 11:59
    '有疑问请教:
    Sub 删除多余重复数据2() '整行

    Dim su As Variant
    Dim a As Long
    Dim b As Long

    a = [c65530].End(xlUp).Row
    b = 0 '如果将 b = b - 1语句 放在Delete前面则应设为b=1
    '赋值从C1开始,因为要保持单元格行号和数组行号一致
    su = Range([c1], Cells(a, 3)) ' 将值转到数组

    For i = 3 To a '首两行是标题行,所以从3开始查
    '我的疑问在此:
    '下行的Range("C:C")上,为什么不可以换成数组范围的形式?即换成su
    If Application.CountIf(Range("C:C"), su(i, 1)) > 1 Then '查C列

    Cells(i + b, 3).EntireRow.Delete
    b = b - 1 '因为删除了一行所以要-1查回此行
    End If

    Next i


    End Sub
      回复  引用    
  91. #291楼 supperST[未注册用户]2006-07-15 15:46
    马老师你好:

    通过循环运算,变量a被赋值,而且是个超长的文本,2千至3千字

    问题:如何把a值复制到粘贴板上?便于我粘贴到其它程序上面。

    ( 如果将a赋值给单元格,再copy单元格,只能复制到头256个字符的文字,无法复制到全部!!)
     
      回复  引用    
  92. #292楼[楼主] 马维峰      2006-07-16 00:22
    @supperST
    Range("C:C")表示整行。
      回复  引用  查看    
  93. #293楼[楼主] 马维峰      2006-07-16 00:25
    @supperST
    Dim MyData as DataObject
    MyData.SetText( range("a1").value)
      回复  引用  查看    
  94. #294楼 supperST [未注册用户]2006-07-22 00:32
    Sub dfdf()
    Dim MyData As DataObject
    MyData.SetText (Range("a1").Value)
    End Sub

    提示用户定义类型未定义?
      回复  引用    
  95. #295楼[楼主] 马维峰      2006-07-22 01:09
    @supperST
    DataObject需要在窗体中定义。如果在其他地方使用,可以增加一个窗体,定义这个对象为Public,然后使用。

    Dim MyData As New DataObject
    MyData.SetText CStr(Range("a1").Value)
    MyData.PutInClipboard
      回复  引用  查看    
  96. #296楼 supperST [未注册用户]2006-07-22 10:49
    初步测试OK,很感谢!

    还有个问题困扰很久了:
    excel导出txt文件后,我用shell方法打开其它程序,那个其实是查询程序,每次是重复的操作动作导入那个txt文件进行批量查询,我希望一步到位……

    但那个程序没有快捷键,无法用sendkeys的方法控制按键;唯有曲线救国——控制鼠标动作,我用过按键精灵,它的控制动作就是用VBS和键盘语做的,我能用它来定位屏幕鼠标位置,但不知在VBA里用什么方法控制??

    希望在VBA实现:
    1、鼠标移动到指定屏幕坐标
    2、左键单击
    3、左键双击
    4、延时控制


    提供按键精灵部分脚本参考:

    #这是按键精灵生成的脚本源文件
    #在理解各条命令的基础上,您可以修改这个文件的内容
    #您修改的结果将会在普通视图上有所反映

    KeyDown 17 1
    KeyPress 65 1
    MoveTo 269 672
    LeftClick 1
    KeyPress 65 1
    MoveTo 269 672
    LeftClick 1
    KeyPress 65 1
    MoveTo 269 672
    LeftClick 1
      回复  引用    
  97. #297楼[楼主] 马维峰      2006-07-22 11:32
    @supperST
    VBA下可以调用VBS脚本,使用Script控件。
      回复  引用  查看    
  98. #298楼 supperST [未注册用户]2006-07-22 13:30
    @马维峰
    马老师:
    能说具体点吗?我看过帮助没看懂

    “使用Script控件。”是不是在“引用”里选,如果是要选哪项?
    希望在VBA实现:
    1、鼠标移动到指定屏幕坐标
    2、左键单击
    3、左键双击
    4、延时控制
      回复  引用    
  99. #299楼 analyse excel[未注册用户]2006-07-28 22:24
    马老师:
    您好!
    请问怎么样在EXCEL中将数据(包括小数在内)转换成大写字母(我也是用于会计上的)?我在上面看到一个叫SUMMER的朋友问了这个问题,但是没有看到具体的回复,请您帮我解决这个难题,谢谢!
      回复  引用    
  100. #300楼 sunny-fine[未注册用户]2006-07-31 11:54
    马老师,您好,

    sub 输出()
    e=36803.77*100
    i=int(e)
    msgbox i
    end sub

    输出的对话框中 i 的值应该是3680377才对,可是不知道为什么却是3680376,我实验了好几遍,不知道为什么,请教马哥给解答一下。
      回复  引用    
  101. #301楼 sunny-fine[未注册用户]2006-07-31 12:12
    sub 输出()
    e=36803.77*100
    i=int(e)
    msgbox i
    end sub

    正常输出的 i 的值应该是3680377才对,而实际输出的却是3680376,不知道为什么,请教马老师给指点一下,困扰了一个上午被这个问题。
      回复  引用    
  102. 评论共3页: 上一页 1 2 3 



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 71504 hmB6Iv0iqUI=



相关文章:

相关链接: