我需要针对这些规则验证字符串:
s
a-z0-9-_/
/
/
/s/
//
s/
/s
(更简单地说,我正在寻找类似于UNIX风格路径的东西,带有斜杠分隔符,其中文件/文件夹名称仅允许a-z0-9-_
,没有文件/文件夹被命名为s
,它没有开头或尾随斜线。)
我需要在客户端通过JavaScript执行此操作,在服务器端使用PHP执行此操作。
我知道最优雅的解决方案将通过复杂的正则表达式。但尝试写一个是否值得挑战?或者我应该只使用条件?
目前,我的解决方案是:http://jsfiddle.net/cKfnW/
JavaScript的:
(function ($) {
var test = function (val) {
return
val != 's' &&
/^[a-z0-9-_\/]+$/.test(val) &&
val.substr(0, 1) != '/' &&
val.substr(val.length-1) != '/' &&
val.search('/s/') == -1 &&
val.search('//') == -1 &&
val.substr(0, 2) != 's/' &&
val.substr(val.length-2) != '/s';
};
$('#test')
.keyup(function () {
if (test($(this).val())) {
$(this).removeClass('fail').addClass('pass');
}
else {
$(this).removeClass('pass').addClass('fail');
}
)
.keyup();
})(jQuery);
PHP:
<?php
function test ($val) {
return
$val != 's' &&
preg_match('/^[a-z0-9-_\/]+$/', $val) &&
substr($val, 0, 1) != '/' &&
substr($val, -1) != '/' &&
strpos($val, '/s/') === false &&
strpos($val, '//') === false &&
substr($val, 0, 2) != 's/' &&
substr($val, -2) != '/s';
}
die (test($_GET['test']) ? 'pass' : 'fail');
?>
这是可接受的做法吗?我不是很擅长正则表达式,而且我不知道如何为此写一个 - 但我不能感觉这更像是一个黑客而不是一个解决方案。
您怎么看?
感谢。
答案 0 :(得分:2)
即使你的支票,你肯定应该通过将它们全部合并为一个来摆脱嵌套IF。 这里有2个regexp的简单变体(首先限制你的边缘情况,第二个检查允许的字符):
if (
$val != 's'
&& !preg_match('!(^/|/s|s/|//|/$)!', $val)
&& preg_match('!^[a-z0-9-_/]+$!', $val)
) {
// ...
}
UPD: 哦,你在键入答案的时候删除了嵌套的IF :) 好,好!
答案 1 :(得分:1)
显然使用正则表达式:
if (preg_match('~^(?!s?/|s$)(?>[a-z0-9_-]++|/(?!s?/|s?$))++$~', $val)) {
// do that
}
模式细节:
~ # pattern delimiter
^ # start of the string
(?!s?/|s$) # negative lookahead (not followed by "s$", "/", "s/")
(?> # open an atomic group (can be replaced by "(?:")
[a-z0-9_-]++ # allowed characters except "/", one or more times
| # OR
/(?!s?/|s?$) # "/" not followed by "s/" or "/" or "$" or "s$"
)++ # close the group and repeat one or more times
$ # end of the string
~ # pattern delimiter
单个正则表达式对多个小正则表达式的优势是什么?
你只测试一次测试字符串,并且第一个坏字符的模式失败。
对于未来调试,您可以使用详细模式和nowdoc使其更清晰,例如:
$pattern = <<<'LOD'
~
^
(?!s?/|s$) # not followed by "s$", "/", "s/"
(?> [a-z0-9_-]++ | / (?!s?/|s?$) )++
$
~x
LOD;
对于客户端,您可以在javascript中使用此模式:
/^(?!s?\/|s$)(?:[a-z0-9_-]|\/(?!s?\/|s?$))+$/
注意:当你想在字符类中放置文字-
时,你必须始终在类的开头或结尾写它,因为它是一个特殊的字符,用于定义一个字符范围。
答案 2 :(得分:1)
AND
'ed要求的单一正则表达式解决方案这是一个符合您要求的注释php正则表达式:(总是以这种方式编写非平凡的正则表达式)
$re = '% # Validate *nix-like path w/multiple specs.
^ # Anchor to start of string.
(?!s$) # Value is not s
(?=.) # Value is at least 1 character long
(?!/) # Value does not begin with /
(?!.*/$) # Value does not end with /
(?!.*/s/) # Value does not contain /s/
(?!.*//) # Value does not contain //
(?!s/) # Value does not begin with s/
(?!.*/s$) # Value does not end with /s
[\w\-/]+ # Value contains only a-z0-9-_/
$ # Anchor to end of string.
%ix';
以下是等效的JavaScript版本:
var re = /^(?!s$)(?=.)(?!\/)(?!.*\/$)(?!.*\/s\/)(?!.*\/\/)(?!s\/)(?!.*\/s$)[\w\-\/]+$/i;
此解决方案假定您的要求不区分大小写。如果不是这种情况,请删除i
ignorecase修饰符(并将[\w\-/]+
表达式更改为[a-z0-9_\-/]+
)。
为了清楚起见,我已经为每个要求编写了注释版本,每行有一个断言。与开头的^
锚点一起,每个前瞻断言都以逻辑AND
方式工作。请注意,(?=.)
断言(确保存在一个字符)是冗余且不必要的,因为最后一个表达式:[\w\-/]+
也确保长度至少为1。请注意,^
和$
锚点都需要这样才能生效。
此解决方案演示了如何在单个易于阅读和维护的正则表达式中实现多个要求。但是,出于其他原因,您可能希望将其拆分为单独的检查 - 例如这样您的代码就可以为每个需求生成单独的有意义的错误消息。