望大佬们提供更加好的解决办法,有人会问这个使用场景,比如 数据库字段你存一个用户的爱好集合,你使用json ["xx","xx"] 还是希望用一个int 来存集合?
目前我使用两种方案。
第一种是转换为 二进制字符串,然后从右往左 按照位是否是==1 来标记集合.
第二种使用 按位与& 来实现,是否 爱好和 用户int 命中,如果两者的任意位 命中,那么 a&b 一定>0 也就是 !=false 也就是 !==0b0

目前我认为 的 比较佳的解决方案,

代码
<?php
$map=[0=>"唱歌a",1=>"跳舞",2=>"洗澡",7=>'睡觉a',8=>'吃饭'];
$decNum=129; // 010000001 (len=9,max_index=8) ; result=["唱歌","吃饭"]
$bin= decbin($decNum);
$mapKeys=array_keys($map);
$maxIndex=$mapKeys;
ksort($maxIndex);
$maxIndex=array_pop($maxIndex)+1;
echo '转2进制(缩写):'.$bin."</br>";
echo '转2进制(补全最大位):'.sprintf("%0".$maxIndex."s",$bin)."</br>";
function strMethod($dec,$map){
//取最大位数+1
$mapKeys=array_keys($map);
$maxIndex=$mapKeys;
ksort($maxIndex);
$str= decbin($dec);
$result=[];
//吧 0b1000 反转为 0b0001 ,因为 mapkey的第0=> 是指 从右往左的 下标
$str=strrev($str);
for($i=0;$i<=strlen($str);$i++){
if(\in_array($i,$mapKeys) && intval($str[$i])===0b1){
$result[]=$map[$i];
}
}
return $result;
}
function bitOperation($dec,$map){
$mapKeys=array_keys($map);
$maxIndex=$mapKeys;
ksort($maxIndex);
$maxIndex=array_pop($maxIndex)+0b1;
/**
*
* array_filter结果是反转k=>v; { ["唱歌"]=> int(0) ["跳舞"]=> int(1) ["洗澡"]=> int(2) ["睡觉"]=> int(7) ["吃饭"]=> int(8) }
*
* array_filter(array,function($MapItemValue){
//return true 就是不过滤,false为过滤
//假设第1伦是 $MapItemValue=0 ; //(2== 0b10)
* 唱歌 0b0+0b1=0b1=1
0b 1
0b010000001
* result : &按位与 两个数二进制部分 相同索引的都是1才能 !=false (0b0),所以这个是1 >0
* 跳舞 0b01+0b01= 0b10=2
0b 10
0b010000001
* })
*/
return array_filter(array_flip($map),function($MapItemValue)use ($dec,$maxIndex){
// echo '本次 $MapItemValue='.sprintf("%0".$maxIndex."s",decbin(1<<$MapItemValue));
// echo ';dec='.sprintf("%0".$maxIndex."s",decbin($dec));
// echo ';&结果'. (decbin(1<<$MapItemValue)&$dec).';';
//这里犯错 (1<<$MapItemValue)&$dec!==0b00000; //这里会优先 $dec!=0b0001 然后在&
// echo ((1<<$MapItemValue)&$dec)!==0b00000;
// echo "<br>";
return ((1<<$MapItemValue)&$dec)!==0b00000;
});
}
$result1=strMethod($decNum,$map);
var_dump($result1);
echo "<br>";
$result2=bitOperation($decNum,$map);
var_dump(array_keys($result2));
对于虎绿林的权限,我选择预定义常量 + 整数 + 位操作,因为权限的个数是固定的。
注意别用MySQL的
bit
类型,那是自找麻烦(写SQL时有类型转换问题),使用int UNSIGNED
类型就可以避免,如果超过32位就用bigint UNSIGNED
。https://gitee.com/hu60t/hu60wap6/blob/master/src/class/userinfo.php
https://github.com/hu60t/hu60wap6/blob/master/src/class/userinfo.php
对于用户的爱好集合,我选择存成单独的表。
这样才方便对爱好列表进行动态更新。
```
$sql = 'UPDATE `'.DB_A.'user` SET `permission` = `permission` & ~ ? WHERE uid = ?';
```
字段还可以这样玩吗?这个 `permission` = `permission` & ~ ?
@胡椒舰长,MySQL位运算符:
a & b
a | b
~ a
a ^ b
a << b
a >> b
a & ~ b
表示“a与非b”,先反转b,然后再与上a,这样就从a中去掉了b。注意:a和b应该同为int,或者同为bit。如果一个为bit一个为int,结果不正确。所以我在上面建议只使用int,因为int的表达更简单。
<!-- markdown -->
> 注意:a和b应该同为int,或者同为bit。如果一个为bit一个为int,结果不正确。所以我在上面建议只使用int,因为int的表达更简单。
我的理解 bit 是 代码 `0b0010` ,而 int 默认是10进制 `2` ,这两个表达式的区别吗?我一直认为 bit和int 好像区别不大,反而是string则是 array[ bit,bit,bit]
@胡椒舰长,你可以选择自动加
https://hu60.cn/q.php/bbs.topic.83135.html
标记必须出现在顶部,否则没有效果。
只为了解它的人开启它,以免产生*号消失的困扰。
@胡椒舰长,PHP没有原生bitset类型,只能以int发送和接收参数。在字段为bit但预处理参数为int时,结果不正确,我不知道为什么。因为PHP没有bitset,所以解决该问题的最简单办法显然是把类型统一到int。
@胡椒舰长,php中的
0b0010
依然是int
类型,因为它有固定的宽度(32位或64位)。而bitset(MySQL中的bit
类型)具有自定义宽度。@胡椒舰长,我认为若干方案及其适合场景:
bigint
位运算json
字段[^1]:
MySQL
数据库InnoDB
引擎Compact
或Dynamic
格式下,按照老虎在 3楼 的用户的爱好
表结构(假设uid
和爱好id
都是主键),较差情况下(全部是随机插入),每存某个人的一个爱好,平均会占用 52 字节。@胡椒舰长,可以查询“某人是否爱好xx”,且此时比
单独成表
快但若你要“查找所有爱好xx的人”的话,就要扫全表了
@胡椒舰长,所以就看你需求啦,
单独成表
xxxINT UNSIGNED
字段json
字段这是我的理解