一、range()解决的是什么问题
写代码时经常需要一串连续的值:生成1到100的编号、构造a到z的字母表、按10为间隔取刻度点。这些需求如果都用for循环手动push,代码会又长又容易出错。range()把这个过程压缩成一次函数调用,直接返回一个排好序的数组。
它的定位是生成器,不是遍历工具。拿到数组之后你可以print_r、foreach、array_map,也可以直接当参数传给别的函数。
二、函数原型与参数
range(string|int|float $start, string|int|float $end, int|float $step = 1): array
| 参数 | 说明 | 是否必需 |
|---|---|---|
| start | 区间起始值 | 必需 |
| end | 区间结束值 | 必需 |
| step | 步长,默认为1 | 可选 |
返回值:一个包含区间内所有元素的数组,元素类型与起止值保持一致。
三个参数都支持int、float和单字符string。step为正数时从start递增到end,step为负数时从start递减到end。step不能为0,传0会触发E_WARNING并返回false。
三、数字区间的生成
最基础的用法:
<?php
$numbers = range(1, 5);
print_r($numbers);
输出:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
)
起点和终点都包含在结果里,这是range()与很多语言里左闭右开区间不同的地方。写分页、生成序号时要注意这个"两端都算"的特性。
指定步长:
<?php
foreach (range(0, 100, 10) as $number) {
echo $number . ", ";
}
输出:
0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
步长为10时,序列按10递增。如果终点不能被步长整除,比如range(0,95,10),之后一个元素是90,不会为了凑到95而多出一个元素。这一点和"取到不超过终点的较大值"一致。
四、字母区间的生成
range()对单字符的处理比较特别,它会按ASCII顺序推进:
<?php
$alphabet = range('a', 'g');
print_r($alphabet);
输出:
Array
(
[0] => a
[1] => b
[2] => c
[3] => d
[4] => e
[5] => f
[6] => g
)
反向也支持:
<?php
$array = range('z', 'u');
print_r($array);
输出:
Array
(
[0] => z
[1] => y
[2] => x
[3] => w
[4] => v
[5] => u
)
起点大于终点时,range()会自动按递减方向生成,不需要把step写成负数。这一点比手写for循环省事。
五、几个容易踩坑的地方
坑一:字母序列只能单字符。传range('a','z')没问题,传range('aa','ad')结果不符合直觉。PHP对多字符字符串的处理是按字符串比较规则来的,不会把它当成"字母表扩展"。需要多字符序列时,得自己用循环拼。
坑二:浮点步长的精度。range(0,1,0.1)这类调用,由于浮点数本身无法精确表示0.1,生成的元素可能带上很长的尾数。项目里如果需要精确的小数刻度,建议先生成整数再用除法换算,比如range(0,10)后每个元素除以10。
坑三:step为0。传0会报警告并返回false,后续foreach会报错。调用前应确认step不为0。
坑四:大区间吃内存。range(1,10000000)会一次性生成一千万个元素的数组,内存直接顶上去。PHP里没有惰性求值的range,需要处理大区间时改用for循环或生成器函数逐个产出。
个人经验:曾经在一个报表导出功能里用range(1,500000)构造行号,本地测试没问题,上线后遇到大数据量直接把内存打满。后来改成在循环里用$i自增,内存占用立刻降下来。range()适合小规模区间,大规模区间还是循环更稳妥。
六、项目实例
实例一:代码号学习编程·生成分页页码
<?php
$totalPages = 8;
$pages = range(1, $totalPages);
foreach ($pages as $page) {
echo "<a href='?page={$page}'>{$page}</a> ";
}
页码是连续的整数序列,range()一行就能拿到,比for循环加push简洁。
实例二:代码号学习编程·构造字母索引
<?php
$letters = range('A', 'F');
foreach ($letters as $letter) {
echo $letter . " 选项\n";
}
用于生成选择题选项、表格列标等场景。
实例三:代码号学习编程·按步长取刻度
<?php
$scale = range(0, 50, 5);
print_r($scale);
输出:
Array
(
[0] => 0
[1] => 5
[2] => 10
[3] => 15
[4] => 20
[5] => 25
[6] => 30
[7] => 35
[8] => 40
[9] => 45
[10] => 50
)
适合图表刻度、时间间隔等场景。
七、range()与for循环的取舍
| 对比项 | range() | for循环 |
|---|---|---|
| 代码长度 | 一行 | 需要初始化、条件、自增 |
| 内存占用 | 一次性生成整个数组 | 逐个处理,占用低 |
| 适用规模 | 小到中等区间 | 任意规模 |
| 可读性 | 表达"区间"语义清晰 | 表达"迭代"更直接 |
选择原则:区间不大、需要把结果当数组用时,用range();区间很大、只需要逐个处理时,用for循环或生成器。不要为了少写几行代码,把一个几百万元素的区间塞进数组。
八、本节课程知识要点
-
range()返回包含起点和终点的数组,两端都算。
-
step控制递增或递减幅度,默认为1,传0会警告并返回false。
-
起点大于终点时自动按递减方向生成,不必手写负步长。
-
字母区间只对单字符有效,多字符字符串的行为不符合直觉。
-
浮点步长存在精度问题,精确小数刻度建议先整数后换算。
-
大区间会占用较多内存,规模大时改用for循环或生成器。
九、常见问题
问:range(5,1)会报错吗?
不会。会自动生成5、4、3、2、1的递减序列。
问:range()生成的数组键名是怎样的?
从0开始的连续整数键,与元素值无关。
问:能不能用range()生成日期序列?
不能直接生成。需要先生成时间戳或天数区间,再用date()换算成日期。比如range(0,6)配合strtotime生成最近七天的日期。
问:step可以是小数吗?
可以,但要注意浮点精度。0.1、0.2这类值在二进制下无法精确表示,结果可能带尾差。