提问人:David Pham 提问时间:5/27/2023 最后编辑:mickmackusaDavid Pham 更新时间:5/27/2023 访问量:134
通过从字符串中间删除带分隔符的单词,将 slug 截断到最大长度
Truncating a slug to maximum length by removing delimited words from the middle of string
问:
我试图通过在必要时从字符串中间删除单词来确保 slug 格式的字符串在总字符限制范围内。
样品蛞蝓:
'/job/hello-this-is-my-job-posting-for-a-daycare-im-looking-for-in-91770-rosemead-california-12345'
字符串将始终以 开头,以 结尾。但是,slug 有 150 个字符的限制,我希望一次截断一个邮政编码之前的单词,这样就不会超过这个字符限制。我知道我必须使用正则表达式/分解,但我该怎么做呢?我尝试了以下方法,但我的 matches 数组似乎有太多元素。/job/
in-zipcode-city-state-job_id
$pattern = '/-in-\d{5}-(.*)-(.*)-(\d*)/';
$string = '/job/hello-this-is-my-job-posting-for-a-daycare-im-looking-for-in-91770-rosemead-california-12345';
preg_match($pattern, $string, $matches);
print_r($matches);
// Array
(
[0] => -in-91770-rosemead-california-12345
[1] => rosemead
[2] => california
[3] => 12345
)
为什么 , 被视为匹配项?不应该只有第一个元素吗?rosemead
california
12345
如何确保完整的 slug 长度不超过 150 个字符,尾随部分(位置)全部包含在内,并在必要时截断前导部分(作业名称)?
答:
您可以在不使用和迭代的情况下执行此操作,只需使用一些标准的字符串操作即可:explode()
$pattern = '/-in-\d{5}-.*-.*-\d*/';
$string = '/job/hello-this-is-my-job-posting-for-a-daycare-im-looking-for-in-91770-rosemead-california-12345';
$matches = [];
if (!preg_match($pattern, $string, $matches)) {
// mismatched string - error handling here
}
$totalLength = 150;
$maxPrefixLength = $totalLength - strlen($matches[0]);
if ($maxPrefixLength < strlen('/job/')) {
// no prefix words possible at all - error handling here
}
$prefixLength = max(strlen('/job/'), strrpos(substr($string, 0, $maxPrefixLength), '-'));
$slug = substr($string, 0, $prefixLength) . $matches[0];
评论
-in-\d{5}-.*-.*-\d*
$prefixLength = max(
max
strlen
strlen
strrpos(...)
/job/foobardodododo
strrpos
false
/job/
/job/
strlen('/job/')
可以通过多种方式将 URL 段的前导部分修剪为指定长度,其中一些方法比其他方法更复杂。这是一个灵活的实用函数,带有信息丰富的注释。我们使用提取前导部分(作业名称)和尾随部分(位置)的正则表达式作为起点。然后,根据允许的总长度减去位置段长度来计算作业名称的最大允许长度。请参阅评论以获取更多见解。
function trim_slug(string $slug, int $maxlen = 150): string
{
// check if trimming is required:
if(strlen($slug) <= $maxlen) {
return $slug;
}
$pattern = '/^(?<job>.+)(?<loc>-in-\d{5}-.*-.*-\d*)$/';
// $match will have 'job' and 'loc' named keys with the matched values
preg_match($pattern, $slug, $match);
// raw cut of job name to maximum length:
$max_job_chars = $maxlen - strlen($match['loc']);
$job_name = substr($match['job'], 0, $max_job_chars);
// tidy up to last delimiter, if exists, instead of mincing words:
if($last_delim = strrpos($job_name, '-')) {
$job_name = substr($match['job'], 0, $last_delim);
}
return $job_name . $match['loc'];
}
$string = '/job/hello-this-is-my-job-posting-for-a-daycare-im-looking-for-in-91770-rosemead-california-12345';
echo trim_slug($string, 80);
// result: /job/hello-this-is-my-job-posting-for-a-in-91770-rosemead-california-12345
在用法示例中,最大长度为 80,因为示例字符串只有 97 个字符,因此将按原样从函数返回,默认限制为 150 个字符。3v4l 演示。
请注意,此答案使用非多字节感知的 PHP 标准字符串函数。如果需要多字节内容,则应使用相应的多字节字符串函数来避免数据损坏。(你是否希望在 URL 段中开始使用多字节字符,以及处理它的最佳方法是什么,这是另一个问题的主题。
- 将输入段子解析为它的 3 个关键组件,
- 通过从总余量中减去第一个和第三个长度来计算中间部分允许的字符数,
- 在达到字符限制之前,通过找到最新出现的连字符来截断中间部分(干净),然后删除剩余的消耗性子字符串。
这样一来,您就可以获得一个优化为最大长度的字符串,而不会损坏 slug 中的整个单词。
代码:(演示)
$slug = '/job/hello-this-is-my-job-posting-for-a-daycare-im-looking-for-in-91770-rosemead-california-12345';
$slugLimit = 70;
echo preg_replace_callback(
'~^(/job/)((?:[^-]*-)*)(in-\d{5}-[^-]*-[^-]*-\d*)$~u',
fn($m) => implode([
$m[1],
preg_replace(
'~^.{0,' . ($slugLimit - mb_strlen($m[1] . $m[3]) - 1) . '}-\K.*~u',
'',
$m[2]
),
$m[3]
]),
$slug
);
输出 slug 的总长度为 68 个字符:
/job/hello-this-is-my-job-posting-in-91770-rosemead-california-12345
或者将第一个和第二个组件组合在一起以简化处理:(演示)
echo preg_replace_callback(
'~^((?:[^-]*-)*)(in-\d{5}-[^-]*-[^-]*-\d*)$~u',
fn($m) => implode([
preg_replace(
'~^.{0,' . ($slugLimit - mb_strlen($m[2]) - 1) . '}-\K.*~u',
'',
$m[1]
),
$m[2]
]),
$slug
);
最后,我能想到的最紧凑的版本在前瞻中使用捕获组,以便在回调中替换完整的字符串匹配项。演示
echo preg_replace_callback(
'~^(?:[^-]*-)*(?=(in-\d{5}-[^-]*-[^-]*-\d*)$)~u',
fn($m) => preg_replace(
'~^.{0,' . ($slugLimit - mb_strlen($m[1]) - 1) . '}-\K.*~u',
'',
$m[0]
),
$slug
);
如果您检查了新的 slug 并且它仍然超出限制,则应抛出异常或通知用户违规。mb_strlen()
评论
$matches
$matches
?:
$pattern = '/-in-\d{5}-(?:.*)-(?:.*)-(?:\d*)/'