Java 如何在不使用正则表达式的情况下检查字符串格式?
我正在做一个项目,我需要检查字符串的格式是否正确ABC1234表示3个字母后跟4个数字。我被告知不要用正则表达式来解决这个问题 我提出了以下代码,但它很笨重,所以我正在寻找更干净、更高效的东西Java 如何在不使用正则表达式的情况下检查字符串格式?,java,Java,我正在做一个项目,我需要检查字符串的格式是否正确ABC1234表示3个字母后跟4个数字。我被告知不要用正则表达式来解决这个问题 我提出了以下代码,但它很笨重,所以我正在寻找更干净、更高效的东西 String sample = ABC1234 char[] chars = sample.toCharArray(); if(Character.isLetter(chars[0]) && Character.isLetter(chars[1]) && Cha
String sample = ABC1234
char[] chars = sample.toCharArray();
if(Character.isLetter(chars[0]) && Character.isLetter(chars[1]) &&
Character.isLetter(chars[2]) && Character.isDigit(chars[3]) &&
Character.isDigit(chars[4]) && Character.isDigit(chars[5]) &&
Character.isDigit(chars[6])){
list.add(sample);
}
// OUTPUT: ABC1234 gets added to "list". When it prints, it appears as ABC1234.
所有输出都如预期的那样,但我知道这可以更有效地完成,或者总体上做得更好
我只是检查前3个字符,确认它们都是字母,最后4个字符应该是数字
有什么建议吗?提前感谢。我将编写两个实用方法;一个用于检查给定的
字符串
是否都是字母,另一个用于检查给定的字符串
是否都是数字。然后使用String.substring(int,int)
调用这两个方法来比较相关的子字符串。像
但是,在实际代码中,正则表达式更好-
Pattern p = Pattern.compile("\\D{3}\\d{4}");
if (p.matcher(s).matches()) {
// ...
}
您可以使用
Integer.parseInt(sample.substring(3,7))一起检查最后4个
不过我想不出一个更快的字母转换方法。Integer.parseInt
将抛出一个NumberFormatException
,如果它不是一个数字,那么在块中尝试一下首先,你唯一能添加的是长度检查
:
if (chars.length == 7 && Character.isLetter(chars[0]) &&
Character.isLetter(chars[1]) && Character.isLetter(chars[2]) &&
Character.isDigit(chars[3]) && Character.isDigit(chars[4]) &&
Character.isDigit(chars[5]) && Character.isDigit(chars[6])) {
//..
}
使用一些循环不会更有效,因为&&
已经短路,并且会在发现您不需要的错误布尔值时停止
char[] chars = sample.toCharArray();
相反,你可以这样做
if(Character.isLetter(sample.charAt(0))
你也可以更花哨一些,做一些类似的事情:
void myFonc(string sample) {
for (int i =0; i < 3; ++i)
if (!Character.isLetter(sample.charAt(i)))
return;
for (int i =3; i < 7; ++i)
if (!Character.isDigit(sample.charAt(i)))
return;
list.add(sample);
}
void myFonc(字符串示例){
对于(int i=0;i<3;++i)
if(!Character.isleter(sample.charAt(i)))
返回;
对于(int i=3;i<7;++i)
if(!Character.isDigit(sample.charAt(i)))
返回;
列表。添加(样本);
}
鉴于您不能使用正则表达式,您当前的方法很好,但这里有另一种方法:
boolean isValid = sample.length() == 7 &&
sample.substring(0, 3).codePoints()
.allMatch(Character::isLetter)
&& sample.substring(3, 7).codePoints()
.allMatch(Character::isDigit);
这是另一种方法
String sample = "ABC1234";
if (sample.substring(0, 3).chars().allMatch(Character::isLetter)
&& sample.substring(3).chars().allMatch(Character::isDigit)) {
list.add(sample);
}
由于问题中包含“所有输出都如预期的那样,但我知道这可以更有效地完成,或者总体上做得更好。”(由于我喜欢性能),我编写了一些基准测试,比较每个答案,以得出关于效率的结论(查看吞吐量)
整个基准代码可以在问题的底部找到,如果您注意到任何错误,我很乐意纠正它(即使它不是完美的,每个答案都有很好的性能指标)
测试在安装了OpenJDK8的DigitalOcean droplet、2GB ram、2个vCore(Intel(R)Xeon(R)CPU E5-2650 v4@2.20GHz
)上运行,JMH版本为1.21
每个答案用3个字符串进行测试,“ABC1234”
以反映问题中的示例,“ABC123D”
应该失败,以及“ABC123”
太短(不确定是否与OP相关).测试配置为5
forks、5
1秒的预热迭代、20
1秒的测量迭代
结果
图表
有两个不同的图表,因为ABC123
图表中的吞吐量大得多(因为某些方法在比较字符串长度后返回false),如果将其添加到吞吐量较小的其余图表中,则无法读取
图表中的数字表示每秒的吞吐量(执行)。
一些注意事项和改进
mrB
由于这不是一个完整的答案(仅用于检查int部分),我使用了@elliotFrisch的字符验证方法。当字符串为ABC1234
时,它当然很快,但是当尝试ABC123D
并捕获NumberFormatException
时,您可以看到性能很差
elliotFrisch
在查看了性能不如其他一些性能快的原因后,虽然可读性很强,但我得出结论,这是因为调用了s.tocharray()
一次用于验证字符,一次用于验证数字
我对此做了改进,因此它只被调用一次,这可以在elliotfrischooptimized
下的结果中看到
azro
很好的解决方案,但是由于调用char[]c=s.tocharray()
然后验证c.length
而不是直接验证s.length()
,因此ABC123
的性能低于其他方法。执行此检查的改进可以在结果中看到mark
太长了,读不下去了
原始代码已经很快了,执行长度检查可以加快速度,如azro
的回答所示。要使此长度检查更快(防止调用s.tocharray()
),请使用标记
如果你想要一个更具可读性/通用性且可重复使用的解决方案,我会选择elliotfrischooptimized
方法,它(几乎)同样快
如果您对性能不太在意(从结果中可以看出,它仍将每秒检查近700万个字符串),那么使用@elliotFrisch提供的正则表达式就可以完成这项工作,它可读性强,易于维护
代码
@Fork(5)
@预热(迭代次数=5次,时间=1次)
@测量(迭代次数=20次,时间=1次)
@状态(Scope.Thread)
公共类MyBenchmark{
@参数({“ABC1234”、“ABC123D”、“AB123”})
串样;
模式p;
int-goodLength;
@设置
公共作废设置(){
this.p=Pattern.compile(“\\D{3}\\D{4}”);
这个.goodLength=7;
}
@基准
公共布尔基线Carlosdelatorre(){
char[]chars=this.sample.toCharArray();
if(Character.isLetter(chars[0])&Character.isLetter(chars[1])&&
Character.islitter(chars[2])&&Character.isDigit(chars[3])&&
Character.isDigit(chars[4])&Character.isDigit(chars[5]))&&
String sample = "ABC1234";
if (sample.substring(0, 3).chars().allMatch(Character::isLetter)
&& sample.substring(3).chars().allMatch(Character::isDigit)) {
list.add(sample);
}
Benchmark (sample) Mode Cnt Score Error Units
MyBenchmark.aomine ABC1234 thrpt 100 5102477.405 ± 92474.543 ops/s
MyBenchmark.aomine ABC123D thrpt 100 5325954.315 ± 118367.303 ops/s
MyBenchmark.aomine AB123 thrpt 100 228544750.370 ± 2972826.551 ops/s
MyBenchmark.azro ABC1234 thrpt 100 38550638.399 ± 582816.997 ops/s
MyBenchmark.azro ABC123D thrpt 100 38159991.786 ± 791457.371 ops/s
MyBenchmark.azro AB123 thrpt 100 76372552.584 ± 1131365.381 ops/s
MyBenchmark.baselineCarlosDeLaTorre ABC1234 thrpt 100 37584463.448 ± 444739.798 ops/s
MyBenchmark.baselineCarlosDeLaTorre ABC123D thrpt 100 38461464.626 ± 461497.068 ops/s
MyBenchmark.baselineCarlosDeLaTorre AB123 thrpt 100 52743609.713 ± 590609.005 ops/s
MyBenchmark.elliotFrisch ABC1234 thrpt 100 16531274.955 ± 313705.782 ops/s
MyBenchmark.elliotFrisch ABC123D thrpt 100 16861377.659 ± 361382.816 ops/s
MyBenchmark.elliotFrisch AB123 thrpt 100 227980231.801 ± 3071776.693 ops/s
MyBenchmark.elliotFrischOptimized ABC1234 thrpt 100 37031168.714 ± 749067.222 ops/s
MyBenchmark.elliotFrischOptimized ABC123D thrpt 100 33383546.778 ± 799217.656 ops/s
MyBenchmark.elliotFrischOptimized AB123 thrpt 100 214954411.915 ± 5283511.503 ops/s
MyBenchmark.elliotFrischRegex ABC1234 thrpt 100 6862779.467 ± 122048.790 ops/s
MyBenchmark.elliotFrischRegex ABC123D thrpt 100 6830229.583 ± 119561.120 ops/s
MyBenchmark.elliotFrischRegex AB123 thrpt 100 10797021.026 ± 558964.833 ops/s
MyBenchmark.mark ABC1234 thrpt 100 38451993.441 ± 478379.375 ops/s
MyBenchmark.mark ABC123D thrpt 100 37667656.659 ± 680548.809 ops/s
MyBenchmark.mark AB123 thrpt 100 228656962.146 ± 2858730.169 ops/s
MyBenchmark.mrB ABC1234 thrpt 100 15490382.831 ± 233777.324 ops/s
MyBenchmark.mrB ABC123D thrpt 100 575122.575 ± 10201.967 ops/s
MyBenchmark.mrB AB123 thrpt 100 231175971.072 ± 2074819.634 ops/s
MyBenchmark.pradipforever ABC1234 thrpt 100 5105663.672 ± 171843.786 ops/s
MyBenchmark.pradipforever ABC123D thrpt 100 5305419.983 ± 80514.769 ops/s
MyBenchmark.pradipforever AB123 thrpt 100 12211850.301 ± 217850.395 ops/s
@Fork(5)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 20, time = 1)
@State(Scope.Thread)
public class MyBenchmark {
@Param({ "ABC1234", "ABC123D", "AB123" })
String sample;
Pattern p;
int goodLength;
@Setup
public void setup() {
this.p = Pattern.compile("\\D{3}\\d{4}");
this.goodLength = 7;
}
@Benchmark
public boolean baselineCarlosDeLaTorre() {
char[] chars = this.sample.toCharArray();
if (Character.isLetter(chars[0]) && Character.isLetter(chars[1]) &&
Character.isLetter(chars[2]) && Character.isDigit(chars[3]) &&
Character.isDigit(chars[4]) && Character.isDigit(chars[5]) &&
Character.isDigit(chars[6])) {
return true;
}
return false;
}
@Benchmark
public boolean mark() {
if (this.sample.length() != this.goodLength) {
return false;
}
char[] chars = this.sample.toCharArray();
return Character.isLetter(chars[0]) && Character.isLetter(chars[1]) &&
Character.isLetter(chars[2]) && Character.isDigit(chars[3]) &&
Character.isDigit(chars[4]) && Character.isDigit(chars[5]) &&
Character.isDigit(chars[6]);
}
@Benchmark
public boolean azro() {
char[] chars = this.sample.toCharArray();
if (chars.length == this.goodLength && Character.isLetter(chars[0]) &&
Character.isLetter(chars[1]) && Character.isLetter(chars[2]) &&
Character.isDigit(chars[3]) && Character.isDigit(chars[4]) &&
Character.isDigit(chars[5]) && Character.isDigit(chars[6])) {
return true;
}
return false;
}
public boolean elliotFrischAllLLettersOptimized(char[] chars, int from, int to) {
for (int i = from; i < to; i++) {
if (!Character.isLetter(chars[i])) {
return false;
}
}
return true;
}
public boolean elliotFrischAllDigitsOptimized(char[] chars, int from, int to) {
for (int i = from; i < to; i++) {
if (!Character.isDigit(chars[i])) {
return false;
}
}
return true;
}
@Benchmark
public boolean elliotFrischOptimized() {
if (this.sample.length() != this.goodLength) {
return false;
}
char[] chars = this.sample.toCharArray();
return elliotFrischAllLLettersOptimized(chars, 0, 3)
&& elliotFrischAllDigitsOptimized(chars, 3, 7);
}
public boolean elliotFrischAllLLetters(String s) {
for (char ch : s.toCharArray()) {
if (!Character.isLetter(ch)) {
return false;
}
}
return true;
}
public boolean elliotFrischAllDigits(String s) {
for (char ch : s.toCharArray()) {
if (!Character.isDigit(ch)) {
return false;
}
}
return true;
}
@Benchmark
public boolean elliotFrisch() {
return this.sample.length() == this.goodLength
&& elliotFrischAllLLetters(this.sample.substring(0, 3))
&& elliotFrischAllDigits(this.sample.substring(3));
}
@Benchmark
public boolean elliotFrischRegex() {
return this.p.matcher(this.sample).matches();
}
@Benchmark
public boolean aomine() {
return this.sample.length() == this.goodLength &&
this.sample.substring(0, 3).codePoints()
.allMatch(Character::isLetter)
&& this.sample.substring(3, 7).codePoints()
.allMatch(Character::isDigit);
}
@Benchmark
public boolean pradipforever() {
if (this.sample.substring(0, 3).chars().allMatch(Character::isLetter)
&& this.sample.substring(3).chars().allMatch(Character::isDigit)) {
return true;
}
return false;
}
public boolean mrBParseInt(String s) {
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException ex) {
return false;
}
}
@Benchmark
public boolean mrB() {
return this.sample.length() == this.goodLength
&& elliotFrischAllLLetters(this.sample.substring(0, 3))
&& mrBParseInt(this.sample.substring(3));
}
}