就在上个月,一个做运营的朋友跟我抱怨,说她在表格里用红黄绿三种颜色标记了一批客户状态,忙活了一下午,最后想按颜色统计一下到底红色几家、绿色几家,结果在Excel里翻遍了函数列表,愣是没找到一个能直接“按颜色数数”的函数。她问我:是不是我用的Excel版本不对?其实不是版本的问题,而是很多人在用“填充颜色”做数据标记的时候,都默认以为表格软件一定能“看懂”颜色。但现实很残酷——Excel原生函数压根不认颜色。
这就要说到今天要聊的color系列函数了。准确地说,Excel并没有一个叫Color的函数,而是围绕颜色统计有一整套“曲线救国”的方案,包括Ribbon界面里的“按颜色排序筛选”、旧版宏表函数GET.CELL,以及VBA下的Interior.Color和Font.Color系列。这套东西的应用场景比你想的要广:考勤表里用颜色标出勤状态、项目看板里用颜色区分任务进度、销售报表里用颜色圈出重点客户,这些都涉及“填充颜色统计”和“字体颜色统计”。
这篇内容我从实际使用角度出发,把color系列函数的三种主流实现方式、完整操作步骤、常见坑,以及几个真实场景的实例全部分享出来。不管你是不想写代码的白板用户,还是愿意用VBA提高效率的进阶玩家,都能找到直接能用的方案。接下来进入正题。
1. 为什么Excel没有原生“按颜色统计”函数
1.1 被颜色标记的数据,是Excel的“盲区”
很多人第一次在Excel里搜索“颜色统计”相关函数时,会发现一个尴尬的事实:官方函数库里根本没有按颜色统计的函数。SumIf、CountIf虽然强大,但它们的条件参数只能匹配数值、文本、日期,不能匹配“单元格颜色”。换句话说,Excel把颜色当成“格式属性”而不是“数据内容”。你在单元格里填了红色背景,但对函数来说,这个红色根本不参与计算。
这就导致一个典型痛点:数据在视觉上已经分好类了,但计算机层面“看不到”分类。你手动数吧,几百行数据眼睛都要瞎了;用筛选吧,如果标记不规范,还得一个个勾颜色。更麻烦的是,如果你把这份表发给别人,对方换个皮肤或主题,颜色可能全变了,统计结果跟着乱套。
1.2 三条主流技术路线:宏表函数、VBA、条件格式辅助
既然原生函数不行,就得靠“旁门左道”。目前业内常用的是三条路线:
| 方案 | 实现方式 | 是否需要代码 | 适用人群 | 局限 |
|---|---|---|---|---|
| 宏表函数GET.CELL | 定义名称,调用旧版Excel 4.0宏函数 | 不用代码,但要用名称管理器 | 不想写VBA的普通用户 | 不能自动刷新;需要启用宏;自定义颜色识别不稳定 |
| VBA自定义函数 | 编写Function过程,读取Interior.Color或Font.Color | 需要写VBA代码 | 进阶用户、需要灵活统计的人 | 需要另存为xlsm;首次信任设置麻烦 |
| 条件格式+辅助列 | 在格式规则中通过公式生成标记值 | 不用代码 | 场景简单、愿意改变习惯 | 只适用于事前规划,事后补救困难 |
我自己在实际项目里,会根据“数据是不是已经定死了”来决定方案。如果表格已经用颜色标记完了,我基本直接上VBA;如果只是临时想看一眼数字,宏表函数GET.CELL够用;如果这张表从零开始搭建,我更推荐从一开始就加一个辅助列,把“颜色代表什么”用文字写进单元格里,这样后期不管是统计还是筛选都方便。不要神化颜色标记,它应该是锦上添花,而不是唯一的数据入口。
2. GET.CELL宏表函数:不写VBA也能统计颜色
2.1 核心机制:借用“老古董”宏表函数识别颜色
GET.CELL是Excel 4.0时期就存在的宏表函数,微软早就把它从公式提示列表里移除了,但底层仍然支持。它的作用是获取指定单元格的格式信息,包括填充色、字体色、边框样式等。函数的语法是GET.CELL(类型号, 单元格),其中比较常用的类型号包括:
- 63:返回单元格的填充背景色编号
- 24:返回单元格的字体颜色编号
- 1:返回单元格的字体名称
- 2:返回字体大小
关于 GET.CELL(63) 返回的“颜色编号”,我这里多说一句。它返回的不是你在调色板里看到的RGB数值(比如红色是255那种),而是当前工作簿调色板里的一个索引值。什么意思呢?如果你用的是Excel默认调色板,红色背景可能返回3,黄色可能返回6,但这些数字在不同环境下可能不同。所以用GET.CELL判断颜色时,通常不是直接跟“红色”比较,而是先让公式识别一次目标色块,再用识别结果去匹配其他单元格。
2.2 定义名称与相对引用的关键细节
宏表函数不能像普通函数一样直接写在单元格里,必须通过“定义名称”来间接使用。步骤如下:
- 选中工作表中的任意一个单元格,比如在A1单元格旁边放一个涂好目标颜色的参考色块(假设在B1涂红色)。
- 切换到“公式”选项卡,点击“定义名称”。
- 名称填“GetFillColor”(自己起,随便),引用位置输入:
注意这里有个关键细节:引用位置中的 Sheet1!A1 必须写成相对引用,不能加美元符号。因为定义名称后,我们在C1单元格输入 =GetFillColor 时,公式会获取当前单元格左侧相邻单元格A1的填充色;在C2输入时,它会自动获取A2的颜色。这就是相对引用的作用。=GET.CELL(63,Sheet1!A1) - 确定后,在C1输入 =GetFillColor,向下拖动填充,就看到一串数字——这就是每个单元格的填充颜色索引值。
同理,要识别字体颜色,就再定义一个名称GetFontColor,引用位置写:
=GET.CELL(24,Sheet1!A1)补充一个坑:定义名称之前,一定要选好“活动单元格”。所谓活动单元格就是你当前选中的那个格子,一般是B1或A1。因为相对引用是相对活动单元格来偏移的,如果活动单元格选得不对,公式偏移的方向就会乱套。最稳妥的做法是:选中A1,再打开定义名称对话框,引用写成=GET.CELL(63,A1),这样公式就表示“获取左侧0列0行的单元格颜色”,也就是A1本身。大家在实际操作时,先单击选中要统计区域左上角那个单元格,再去定义名称。
2.3 用辅助列完成颜色求和与颜色计数
拿到颜色索引号之后,统计就变成普通函数的活路了。比如统计A1:A20中红色背景的单元格数量:
=COUNTIF(C1:C20, GetFillColor)其中 GetFillColor 是你先在某个参考单元格(比如B1)输入=GetFillColor得到的红色索引值。注意,GET.CELL(63)如果单元格没有填充色,返回0。如果单元格是“无填充”,但它套用了表格样式或主题颜色,返回值也可能不是0,这点后面在常见问题里细说。
要按颜色求和,比如计算红色背景单元格对应在B列中的数值总和:
=SUMIF(A1:A20, GetFillColor, B1:B20)这里A列是填充色识别列,B列是数值列。逻辑就是用COUNTIF或SUMIF把颜色索引当作匹配条件。只要颜色索引列生成正确,后面想怎么统计都行。
2.4 宏表函数的刷新机制与保存注意事项
宏表函数有一个区别普通函数的大毛病:修改颜色后,公式结果不会立刻更新。比如你把一个单元格从红色改成绿色,旁边的 =GetFillColor 可能还停留在原来的红色索引值上。这时候需要手动强制重算,快捷键是 Ctrl+Alt+F9。这是我使用宏表函数时踩过最深的坑——统计了半天,发现结果跟看到的颜色对不上,就是因为忘记刷新。
另外,工作簿只要用了宏表函数,就相当于含宏文件。保存格式要是xlsx,在某些旧版本Excel里可能直接报错或提示兼容性问题;在较新版本中,xlsx仍可正常保存,但为了保险起见,建议另存为xlsm或xls格式。分享给别人之前,也要提醒对方启用宏,否则公式可能一打开就失效。
3. VBA自定义color系列函数:灵活统计的进阶方案
3.1 用Interior.Color读取填充色,写出第一个按颜色计数函数
宏表函数用着不用写代码,但灵活性有限,碰到自定义颜色、条件格式、大批量数据时容易翻车。这个时候VBA方案就显示出优势了。VBA里和颜色相关的属性主要是两个:Interior.Color(填充色)和Font.Color(字体色)。这两个属性返回的都是RGB长整型数值,比如红色是255,黄色是65535。因为是RGB值,所以精确度比GET.CELL的索引值要高,自定义颜色也能准确匹配。
打开VBA编辑器的方法:按 Alt+F11,在左侧工程窗口右键选中VBAProject,插入“模块”。在模块里写下:
Function ColorCount(rng As Range, colorCell As Range) As Long Dim cell As Range Dim targetColor As Long Application.Volatile targetColor = colorCell.Interior.Color For Each cell In rng If cell.Interior.Color = targetColor Then ColorCount = ColorCount + 1 End If Next cell End Function这段函数的意思是:你给它两个参数,一个是待统计区域(rng),一个是涂了目标颜色的参考单元格(colorCell),它会把区域内和参考单元格颜色一致的数量返回出来。在单元格里用起来很简单:
=ColorCount(A1:A20, B1)B1就是你涂的那个红色参考块。这个函数的优势在于,颜色值以RGB形式存储在变量里,不管你是主题色、自定义色,还是从别的程序复制过来的颜色,只要RGB值一致,就能匹配上。
3.2 按字体颜色统计与求和函数写法
字体颜色统计的思路和填充色一样,只是把Interior.Color替换成Font.Color。下面给出一个同时支持“填充色”和“字体色”两种统计模式的函数,这样你一个函数就能应对不同的标记习惯:
Function SumByColor(rngData As Range, rngColor As Range, Optional ByVal useFont As Boolean = False) As Double Dim cell As Range Dim targetColor As Long Application.Volatile If useFont Then targetColor = rngColor.Font.Color Else targetColor = rngColor.Interior.Color End If For Each cell In rngData If useFont Then If cell.Font.Color = targetColor Then SumByColor = SumByColor + cell.Value End If Else If cell.Interior.Color = targetColor Then SumByColor = SumByColor + cell.Value End If End If Next cell End Function使用方式:
=SumByColor(B2:B20, E2) // 按填充色求和 =SumByColor(B2:B20, E2, TRUE) // 按字体颜色求和这里把第三个参数命名为 useFont,默认值为False,这样你在输入公式时不写第三个参数,就是按填充色统计;写TRUE,就是按字体颜色统计。我在实际工作中经常遇到一种情况:有人不涂填充色,而是用红色字体标记异常,用蓝色字体标记正常。这种标记方式用上面这个函数就太合适了。
3.3 多条件组合:颜色+数值范围/文本关键词
只判断颜色有时候不够。比如你想统计“红色填充且销售额大于10000的单元格数量”,或者“橙色字体且产品名包含‘A型号’的条目数”,单纯一个颜色函数就没法满足。但这种场景也很常见,我的处理办法是给VBA函数增加条件参数。
Function CountByColorCondition(rngColor As Range, colorCell As Range, _ rngCheck As Range, checkType As String, checkValue As Variant) As Long Dim cell As Range Dim targetColor As Long Dim i As Long Application.Volatile targetColor = colorCell.Interior.Color For i = 1 To rngColor.Cells.Count If rngColor.Cells(i).Interior.Color = targetColor Then Select Case checkType Case ">" If rngCheck.Cells(i).Value > checkValue Then CountByColorCondition = CountByColorCondition + 1 Case "<" If rngCheck.Cells(i).Value < checkValue Then CountByColorCondition = CountByColorCondition + 1 Case "=" If rngCheck.Cells(i).Value = checkValue Then CountByColorCondition = CountByColorCondition + 1 Case "包含" If InStr(rngCheck.Cells(i).Value, checkValue) > 0 Then CountByColorCondition = CountByColorCondition + 1 End Select End If Next i End Function用法举例:如果A列背景色是红色,C列是销售额,想统计“红色且销售额大于10000”的条数:
=CountByColorCondition(A1:A20, E2, C1:C20, ">", 10000)这个函数把颜色判断和数值判断分开在两个区域上,两个区域要求一一对应,所以写公式前务必要确保rngColor和rngCheck区域大小一致。如果区域错位,统计结果会完全错了。刚开始写这类自定义函数时,我自己就犯过区域对不齐的错,最后检查了半天才发现是B列和C列的范围没对齐,低级错误却最坑人。
3.4 自定义函数不能自动重算?用Application.Volatile解决
VBA自定义函数有个怪脾气:在单元格里引用了颜色区域后,你修改背景色,函数结果常常不更新。这就好像Excel察觉不到“颜色变了”这件事。我在前面写的代码里都加了Application.Volatile这行,它的作用是告诉Excel“这种函数需要频繁更新”,让Excel在重新计算时顺带刷新它。加上这行后,只要按F9重算,函数就会重新读取颜色值。
但还是有个细节要提醒:Application.Volatile只保证在Excel“重算”时更新函数,不能用它让函数监控颜色变化事件。所以如果图表或公式不刷新,始终记着一招——Ctrl+Alt+F9强制全量重算。VBA没有留下直接监听颜色变化的完美事件机制,这是所有颜色统计方案的通病,明确了这一点,很多问题就能理解了。
4. 实例应用讲解:考勤表、任务看板与销售报表
4.1 实例一:考勤表按颜色统计出勤状态
先看一个最普遍的考勤场景。某个团队用颜色标记考勤:绿色代表满勤,橙色代表迟到,黄色代表事假,红色代表旷工,灰色代表休息。每天一行,姓名在A列,日期从B列开始往后排。表格做完之后,月底要统计每个人“绿色几个月”“迟到几次”,这时候手工数眼睛肯定看瞎。
如果只有这一张表,最快的方法是用宏表函数GET.CELL。新建一个辅助工作表,先把表头复制过去,然后在B2单元格中定义名称,用GET.CELL(63,A2)的偏移方式生成一行行的颜色识别码。接着用COUNTIF统计每行中绿色出现的次数。
不过说实话,考勤表这种“有规律”的数据结构,我更推荐在第一天就设定好规则:把考勤状态做成下拉列表,再用条件格式把列表里的文字映射成颜色。这样颜色只是可视化的外衣,真正统计时数的是状态文本,用COUNTIF直接算。比如“满勤”出现了几次,就是=COUNTIF(B2:AF2,"满勤")。颜色统计不是不能用,而是这种场景有更好的常规做法。只有当别人已经发给你一张“只有颜色、没有标记文字”的考勤表时,才值得动用color系列函数。
4.2 实例二:项目任务看板统计完成进度
项目看板是另一个很典型的应用场景。看板上每一行是一个任务,状态用填充颜色区分:灰色是未开始,黄色是进行中,绿色是已完成,红色是延期。旁边还有一列预计工时。领导过来问一句:“这周完成了多少工时?”如果任务表里只有颜色标记,没有状态列,那就得用VBA的ColorSum来统计绿色行的工时合计。
我在某公司的项目复盘里就是这么干的。团队用共享表格维护任务进度,颜色标得明明白白,但从没人想着加“状态”列。到月底要做项目周报,才想到要按颜色汇总工时。下面这个公式帮了大忙:
=ColorSum(D2:D100, F1)D列是工时,F1是涂了绿色的参考单元格。函数遍历D2:D100,每行看的是同行的A列颜色,只要A列填充色和F1一致,就把D列的数值累加。这个逻辑的关键点在于:颜色判断区和求和区行号必须对齐。实际写代码时,我建议把VBA函数的判断区设计成“与求和区传入”行号一致的两列,如CountByColor中rngColor和rngData的行号保持一致,否则求和结果会串行。
项目看板场景里,颜色与状态共存是最佳实践。我见过一些优秀的看板模板,直接在状态列预留“隐藏文本”,文本值用条件格式映射到颜色,统计时直接用COUNTIFS汇总。颜色只是给人看的,文本是给函数看的。这个思路希望你可以用到自己的项目里。
4.3 实例三:销售报表标记重点客户与异常数据
销售报表里经常出现一个需求:把核心客户用红色填充突出显示,把风险客户用橙色填充显示,其他普通客户不填颜色。然后月底要求“红色客户一共有几个”“橙色客户贡献了多少销售额”。
这类业务的难点在于,销售数据往往从系统中导出,颜色是事后手工加的,所以它天然适合VBA方案。我在处理某销售部门报表时,写了一个组合的统计脚本,一次性输出四种结果:
- 红色客户数量
- 橙色客户数量
- 红色客户销售额合计
- 橙色客户销售额合计
脚本本质就是在工作表变化时自动更新汇总单元格,但我没有用变化事件,而是直接在汇总区写了四个自定义函数调用。这里给一个简化版示例:
Function CountColor(rng As Range, rgbValue As Long) As Long Dim cell As Range Dim cnt As Long For Each cell In rng If cell.Interior.Color = rgbValue Then cnt = cnt + 1 Next cell CountColor = cnt End Function注意这个函数的第二个参数不是颜色单元格,而是RGB数值。调用时你不能写“红色”,要写255。这在实际操作中不太直观,所以我更常用“参考单元格”方式,而不是直接写RGB值。你可以在报表旁边专门放一行色块区域,每个色块填一种颜色,函数引用色块即可。这样只要维护色块,公式不用改,还方便别人看明白你的统计逻辑。
4.4 实例四:文本表格里的字体颜色统计
填充色统计做好之后,字体颜色的应用场景一样躲不开。最典型的就是评审表:评审人把有问题的项目用红色字体标注,没问题的用黑色字体,最后的评审结论要数“红色标注出了多少个问题”。
字体颜色统计用VBA也很顺手。关键是判断条件从Interior.Color改成Font.Color。前面给的SumByColor函数已经兼顾了这个场景。实际用的时候建议把“标识字体红色”和“标识单元格填充黄色”这两类情况都做成规范,这样统计时一个函数通吃。
我总结半个经验:很多团队里大家标颜色都靠手感和自觉,有人用红色字体,有人用红色填充,还有人用红色边框,统计之前必须先把新口径统一。VBA能统计的只是“你用什么颜色、标在哪个位置”,它读不懂你的心思。拿到的表格如果标记方式五花八门,再好的函数也白搭。
5. 常见问题与排查技巧实录
5.1 宏表函数结果全部为0或没有变化
用GET.CELL统计时遇到最多的问题就是公式结果全是0。排查顺序一般是这样:
- 确认定义名称是否正确,引用位置有没有把单元格写错。
- 确认公式所在单元格和引用单元格的相对位置。如果定义名称写的是=GET.CELL(63,Sheet1!$A$1),那所有公式都会去读A1一个格子的颜色。
- 确认工作簿已经启用宏。
- 如果修改颜色后结果不变,按Ctrl+Alt+F9强制重算。
- 检查是否保存成了xlsx并再次打开,部分旧环境下宏表函数会失效,建议改用xlsm。
5.2 VBA函数返回#NAME?或#VALUE!
自定义函数返回#NAME?,通常有两个原因:一是代码没放到标准模块里,二是函数名写错。插入模块时要注意,在VBA工程窗口中选中VBAProject,右键选择“插入”->“模块”,而不是“类模块”。
返回#VALUE!则多半是参数类型不匹配。比如ColorSum函数要求第二个参数是一个Range,你写成了一个数字,VBA一计算就报错。排查时可以临时在单元格里输入=CountColor(A1:A10, B1),确认B1真的填了颜色,而不是写着“红色”两个字。这个低级错误我真的见过很多次,尤其是在共享工作簿里,别人把参考单元格的内容误删了,填成了文本。
5.3 颜色相近匹配不上:RGB与调色板色差
VBA里用Interior.Color返回RGB值解决大部分匹配问题,但还有一个坑:有些颜色是通过主题色或者条件格式生成的,它的Color值看起来是一个RGB,实际在界面上设置时与肉眼看到的颜色有细微色差。比如“黄色”这个颜色,有人用标准调色板黄色(RGB 255,255,0),有人用Excel主题里的“着色4”(RGB 255,200,0),二者肉眼都叫黄色,但数值完全不同。
最稳妥的匹配方式有二:一是参考单元格法,直接拿一个真实涂好色的格子去匹配;二是在代码里把颜色值近似归一化,比如把RGB拆开,判断三个通道是否都在允许的误差范围内。不过实战里我一般不会把误差范围搞得太复杂,参考单元格法够用了。
5.4 条件格式产生的颜色统计不到
这是一个很多人没意识到的盲区。GET.CELL和VBA的Interior.Color都只能读取“静态填充色”,条件格式动态生成的颜色是不算数的。比如你设置了“数值大于100时整行标红色”,这个红色是条件格式渲染出来的,不是单元格本身的格式,所以颜色统计函数读到的还是原始无填充状态。
解决条件格式颜色统计问题的方案有两个。方案一是不统计颜色,直接数条件:既然你已经有规则“数值大于100”,那统计时直接=COUNTIF(区域,">100"),根本不需要绕道颜色。方案二是使用VBA的DisplayFormat属性读取显示格式,它可以拿到条件格式渲染后的颜色。例如:
If cell.DisplayFormat.Interior.Color = targetColor Then但DisplayFormat不能用于自定义函数里读取单元格格式,在某些Office版本里会报错,稳定性一般。所以我还是推荐方案一:条件格式能覆盖统计需求的,就别去读颜色,老老实实读值。
5.5 性能问题:几千行数据卡成狗
VBA自己写循环读取每一个单元格的Interior.Color,在几千行数据时问题不大,但要是几万行还要逐个求和,速度就会明显变慢。我在帮某团队处理过一张近两万行的明细表,ColorSum函数跑一次要卡十几秒。
优化思路主要有两个。第一个思路是把颜色值一次性读进数组:
Dim colorArr() As Long ReDim colorArr(1 To rng.Cells.Count) For i = 1 To rng.Cells.Count colorArr(i) = rng.Cells(i).Interior.Color Next i这也是逐格读,只是把“读颜色”和“判断求和”分成两个阶段,代码结构更清晰。第二个思路是先用筛选或者辅助列把数据范围缩小,再对筛后的区域做统计。如果只是做一次性统计,完全不建议用VBA函数,直接用筛选+SUBTOTAL更快。我处置大表时,优先会用辅助列配合GET.CELL生成颜色索引,然后用SUMIFS、COUNTIFS处理,速度比分步刷自定义函数还快。
5.6 单元格无填充与白色填充的区分
还有一个容易踩坑的细节。肉眼看着没填颜色的单元格,GET.CELL和VBA读出来的值可能是0,也可能是白色(RGB 16777215)或者无色(-4142,即VBA里的xlNone)。在实际表格里,“无填充”与“填充了白色”是不同的概念,二者在VBA里的返回值也不同。
如果你希望统计“无填充”的单元格,只判断=0是不够的,还要排除-4142和16777215两种情况。这里建议统一约定:默认所有单元格不填充,需要标记的才填颜色。这样统计时只数有颜色值的单元格,逻辑最简单。如果表格里已经混入了“看起来没色其实填了白底”的单元格,建议先全选区域,将填充色改成“无填充”,再重新标记。
5.7 颜色统计在WPS中的差异
补充一个很多人关心的问题:WPS表格能不能用这些color系列函数?根据我自己的测试,WPS对宏表函数GET.CELL的支持比较稳定,定义名称的方法和Excel基本一致;但VBA支持上两者差异较大,WPS默认没有内置VBA编辑器(部分版本可选启用宏),而且自定义函数的兼容性也参差不齐。如果你的团队同时使用Excel和WPS,最好提前确认VBA方案在WPS里能正常跑,否则推荐全部走宏表函数或辅助列方案,这样两边都能用。
写到最后的一些心里话
color系列函数的本质,其实是在跟Excel的“格式与数据分离”机制较劲。颜色是用来给人看的,函数是用来处理数据的,两者天然有一条鸿沟。我用这套方案解决过考勤统计、项目进度汇总、销售报表等多种问题,最大的教训是:颜色标记虽然好看,但真正稳的统计方案一定是以文本或数值为核心,颜色只是可选的视觉辅助。给自己搭表格的时候,能加一列状态文字就尽量加,别让颜色成为唯一的信息载体;拿到别人的表,能先用筛选看一眼数据分布再决定要不要上颜色统计函数,别上来就闷头写公式。
如果你实在躲不开“只有颜色、没有文字”的存量表格,希望这篇文章里的GET.CELL和VBA函数能帮你省下几个小时的眼力。最后再分享一个小技巧:写好的VBA颜色统计函数,建议保存在个人宏工作簿里,这样下次新建任何工作簿都能直接输入函数名调用,不用每次重新插模块。我就是这样把常用的ColorSum、ColorCount、CountByColorCondition三个函数固定下来了,遇到颜色统计需求直接填参数,效率比临时写代码高得多。