我在 C# 中有以下代码:
public static bool IsAlphaAndNumeric(string s)
{
return Regex.IsMatch(s, @"[a-zA-Z]+")
&& Regex.IsMatch(s, @"\d+");
}
我想检查参数s
是否至少包含一个字母和一个数字,我编写了上面的方法来执行此操作。但是,是否有一种方法可以将这两个正则表达式 (
"[a-zA-Z]+"
和 "\d+"
) 组合成一个?对于使用 LINQ 的 C#:
return s.Any(Char.IsDigit) && s.Any(Char.IsLetter);
@"^(?=.*[a-zA-Z])(?=.*\d)"
^ # From the begining of the string
(?=.*[a-zA-Z]) # look forward for any number of chars followed by a letter, don't advance pointer
(?=.*\d) # look forward for any number of chars followed by a digit)
使用两个正向先行断言确保在成功之前找到一个字母和一个数字。添加^
只尝试从字符串开头向前查找一次。否则,正则表达式引擎会尝试在字符串的每个点进行匹配。
虽然不是完全符合您的要求,但假设我有更多时间。以下方法应该比正则表达式更快。
static bool IsAlphaAndNumeric(string str) {
bool hasDigits = false;
bool hasLetters=false;
foreach (char c in str) {
bool isDigit = char.IsDigit(c);
bool isLetter = char.IsLetter(c);
if (!(isDigit | isLetter))
return false;
hasDigits |= isDigit;
hasLetters |= isLetter;
}
return hasDigits && hasLetters;
}
static IEnumerable<string> GenerateTest(int minChars, int maxChars, int setSize) {
string letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
string numbers = "0123456789";
Random rnd = new Random();
int maxStrLength = maxChars-minChars;
float probablityOfLetter = 0.0f;
float probablityInc = 1.0f / setSize;
for (int i = 0; i < setSize; i++) {
probablityOfLetter = probablityOfLetter + probablityInc;
int length = minChars + rnd.Next() % maxStrLength;
char[] str = new char[length];
for (int w = 0; w < length; w++) {
if (probablityOfLetter < rnd.NextDouble())
str[w] = letters[rnd.Next() % letters.Length];
else
str[w] = numbers[rnd.Next() % numbers.Length];
}
yield return new string(str);
}
}
class DarinDimitrovSolution
{
const string regExpression = @"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$";
private static readonly Regex _regex = new Regex(
regExpression, RegexOptions.Compiled);
public static bool IsAlphaAndNumeric_1(string s) {
return _regex.IsMatch(s);
}
public static bool IsAlphaAndNumeric_0(string s) {
return Regex.IsMatch(s, regExpression);
}
static void Main(string[] args) {
int minChars = 3;
int maxChars = 13;
int testSetSize = 5000;
DateTime start = DateTime.Now;
foreach (string testStr in
GenerateTest(minChars, maxChars, testSetSize)) {
IsAlphaNumeric(testStr);
}
Console.WriteLine("My solution : {0}", (DateTime.Now - start).ToString());
start = DateTime.Now;
foreach (string testStr in
GenerateTest(minChars, maxChars, testSetSize)) {
DarinDimitrovSolution.IsAlphaAndNumeric_0(testStr);
}
Console.WriteLine("DarinDimitrov 1 : {0}", (DateTime.Now - start).ToString());
start = DateTime.Now;
foreach (string testStr in
GenerateTest(minChars, maxChars, testSetSize)) {
DarinDimitrovSolution.IsAlphaAndNumeric_1(testStr);
}
Console.WriteLine("DarinDimitrov(compiled) 2 : {0}", (DateTime.Now - start).ToString());
Console.ReadKey();
}
My solution : 00:00:00.0170017 (Gold)
DarinDimitrov 1 : 00:00:00.0320032 (Silver medal)
DarinDimitrov(compiled) 2 : 00:00:00.0440044 (Gold)
int minChars = 20;
int maxChars = 50;
int testSetSize = 100000;
My solution : 00:00:00.4060406
DarinDimitrov 1 : 00:00:00.7400740
DarinDimitrov(compiled) 2 : 00:00:00.3410341 (now that very fast)
我再次检查了使用RegexOptions.IgnoreCase标志的情况,其余参数与上述相同。
My solution : 00:00:00.4290429 (almost same as before)
DarinDimitrov 1 : 00:00:00.9700970 (it have slowed down )
DarinDimitrov(compiled) 2 : 00:00:00.8440844 ( this as well still fast but look at .3 in last result)
在gnarf提到我的算法存在问题后,我进行了更改。它之前只检查字符串是否只包含字母和数字,现在它检查字符串至少应该有一个字符和一个数字。
static bool IsAlphaNumeric(string str) {
bool hasDigits = false;
bool hasLetters = false;
foreach (char c in str) {
hasDigits |= char.IsDigit(c);
hasLetters |= char.IsLetter(c);
if (hasDigits && hasLetters)
return true;
}
return false;
}
结果
My solution : 00:00:00.3900390 (Goody Gold Medal)
DarinDimitrov 1 : 00:00:00.9740974 (Bronze Medal)
DarinDimitrov(compiled) 2 : 00:00:00.8230823 (Silver)
如果你所使用的系统只接受一个正则表达式,你可以使用[a-zA-Z].*[0-9]|[0-9].*[a-zA-Z]
,但我只建议在这种情况下使用。我无法想象这比不带替换的两个简单模式更有效。
private static readonly Regex _regex = new Regex(
@"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$", RegexOptions.Compiled);
public static bool IsAlphaAndNumeric(string s)
{
return _regex.IsMatch(s);
}
RegexOptions.Compiled | RegexOptions.IgnoreCase
。RegexOptions.IgnoreCase
,则无需使用 (?=.*[A-Z])
的 lookahead。 - kennytm以下不仅比其他向前查看结构更快,而且(在我看来)更接近需求:
[a-zA-Z\d]((?<=\d)[^a-zA-Z]*[a-zA-Z]|[^\d]*\d)
在我的(虽然粗略的)测试中,它运行所需时间比其他正则表达式解决方案少了一半,并且具有这样的优点:它不会关心输入字符串中的换行符。 (如果由于某种原因它应该关心,那么如何包含它是显而易见的)。
以下是它的工作原理:
步骤1:它匹配一个单个字符(我们称之为c),该字符是数字或字母。
步骤2:它进行回溯以检查c是否为数字。 如果是:
步骤2.1:它允许无限数量的非字母字符,后跟一个字母。 如果匹配成功,则有一个数字(c)后跟一个字母。
步骤2.2:如果c不是数字,则必须是字母(否则它不会被匹配)。 在这种情况下,我们允许无限数量的非数字,后跟一个数字。 这意味着我们有一个字母(c)后跟一个数字。
^(?>[^A-Za-z0-9]*)
来完全避免回溯问题。这样做后,我认为回顾将不再起到作用。为了获得最佳性能,我会选择 ^(?>[^A-Za-z0-9]*)(?:[a-zA-Z](?>[^0-9]*)[0-9]|[0-9](?>[^A-Za-z]*)[a-zA-Z])
。如果我担心性能的话... ;) - Alan Moore
A1
和!@#1%^&A()_
都可以通过检查--这是你想要的吗? - Alan Moore