【发布时间】:2011-03-31 21:59:38
【问题描述】:
我正在尝试创建一个正则表达式来匹配纬度/经度坐标。为了匹配双精度数,我使用了(\-?\d+(\.\d+)?),并尝试将其组合成一个表达式:
^(\-?\d+(\.\d+)?),\w*(\-?\d+(\.\d+)?)$
我希望这可以匹配一个双精度数、一个逗号、也许一些空格和另一个双精度数,但它似乎不起作用。具体来说,它只有在没有空间的情况下才有效,而不是一个或多个。我做错了什么?
【问题讨论】:
标签: regex
我正在尝试创建一个正则表达式来匹配纬度/经度坐标。为了匹配双精度数,我使用了(\-?\d+(\.\d+)?),并尝试将其组合成一个表达式:
^(\-?\d+(\.\d+)?),\w*(\-?\d+(\.\d+)?)$
我希望这可以匹配一个双精度数、一个逗号、也许一些空格和另一个双精度数,但它似乎不起作用。具体来说,它只有在没有空间的情况下才有效,而不是一个或多个。我做错了什么?
【问题讨论】:
标签: regex
这个将严格匹配正确范围内的纬度和经度值:
^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$
匹配
不匹配
【讨论】:
?: 非捕获组语法以及捕获极性 (^[-+]?(?:[1-8]?\d(?:\.\d+)?|90(?:\.0+)?)),\s*([-+]?(?:180(?:\.0+)?|(?:(?:1[0-7]\d)|(?:[1-9]?\d))(?:\.\d+)?))$
我正在使用这些(十进制格式,带 6 个十进制数字):
^(\+|-)?(?:90(?:(?:\.0{1,6})?)|(?:[0-9]|[1-8][0-9])(?:(?:\.[0-9]{1,6})?))$
^(\+|-)?(?:180(?:(?:\.0{1,6})?)|(?:[0-9]|[1-9][0-9]|1[0-7][0-9])(?:(?:\.[0-9]{1,6})?))$
Here 是一个测试两者的要点,也在这里报告,以便于访问。这是一个 Java TestNG 测试。你需要 Slf4j、Hamcrest 和 Lombok 来运行它:
import static org.hamcrest.Matchers.*;
import static org.hamcrest.MatcherAssert.*;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import lombok.extern.slf4j.Slf4j;
import org.testng.annotations.Test;
@Slf4j
public class LatLongValidationTest {
protected static final String LATITUDE_PATTERN="^(\\+|-)?(?:90(?:(?:\\.0{1,6})?)|(?:[0-9]|[1-8][0-9])(?:(?:\\.[0-9]{1,6})?))$";
protected static final String LONGITUDE_PATTERN="^(\\+|-)?(?:180(?:(?:\\.0{1,6})?)|(?:[0-9]|[1-9][0-9]|1[0-7][0-9])(?:(?:\\.[0-9]{1,6})?))$";
@Test
public void latitudeTest(){
DecimalFormat df = new DecimalFormat("#.######");
df.setRoundingMode(RoundingMode.UP);
double step = 0.01;
Double latitudeToTest = -90.0;
while(latitudeToTest <= 90.0){
boolean result = df.format(latitudeToTest).matches(LATITUDE_PATTERN);
log.info("Latitude: tested {}. Result (matches regex): {}", df.format(latitudeToTest), result);
assertThat(result, is(true));
latitudeToTest += step;
}
latitudeToTest = -90.1;
while(latitudeToTest >= -200.0){
boolean result = df.format(latitudeToTest).matches(LATITUDE_PATTERN);
log.info("Latitude: tested {}. Result (matches regex): {}", df.format(latitudeToTest), result);
assertThat(result, is(false));
latitudeToTest -= step;
}
latitudeToTest = 90.01;
while(latitudeToTest <= 200.0){
boolean result = df.format(latitudeToTest).matches(LATITUDE_PATTERN);
log.info("Latitude: tested {}. Result (matches regex): {}", df.format(latitudeToTest), result);
assertThat(result, is(false));
latitudeToTest += step;
}
}
@Test
public void longitudeTest(){
DecimalFormat df = new DecimalFormat("#.######");
df.setRoundingMode(RoundingMode.UP);
double step = 0.01;
Double longitudeToTest = -180.0;
while(longitudeToTest <= 180.0){
boolean result = df.format(longitudeToTest).matches(LONGITUDE_PATTERN);
log.info("Longitude: tested {}. Result (matches regex): {}", df.format(longitudeToTest), result);
assertThat(result, is(true));
longitudeToTest += step;
}
longitudeToTest = -180.01;
while(longitudeToTest >= -300.0){
boolean result = df.format(longitudeToTest).matches(LONGITUDE_PATTERN);
log.info("Longitude: tested {}. Result (matches regex): {}", df.format(longitudeToTest), result);
assertThat(result, is(false));
longitudeToTest -= step;
}
longitudeToTest = 180.01;
while(longitudeToTest <= 300.0){
boolean result = df.format(longitudeToTest).matches(LONGITUDE_PATTERN);
log.info("Longitude: tested {}. Result (matches regex): {}", df.format(longitudeToTest), result);
assertThat(result, is(false));
longitudeToTest += step;
}
}
}
【讨论】:
空格是\s,而不是\w
^(-?\d+(\.\d+)?),\s*(-?\d+(\.\d+)?)$
看看这是否有效
【讨论】:
实际上,Alix Axel,上面的正则表达式在纬度、经度范围的角度是错误的。
纬度测量范围从 –90° 到 +90° 经度测量范围从 –180° 到 +180°
所以下面给出的正则表达式验证更准确。
另外,根据我的想法,没有人应该限制纬度/经度的小数点。
^([-+]?\d{1,2}([.]\d+)?),\s*([-+]?\d{1,3}([.]\d+)?)$
目标 C 的 OR
^([-+]?\\d{1,2}([.]\\d+)?),\\s*([-+]?\\d{1,3}([.]\\d+)?)$
【讨论】:
99 为Latitude,而99 超出-90, +90 的范围,因此无效。
^-?[0-9]{1,3}(?:\.[0-9]{1,10})?$
正则表达式分解:
^-?[0-9]{1,3}(?:\.[0-9]{1,10})?$
-? # 接受负值
^ # 字符串开始
[0-9]{1,3} # 匹配 1-3 位数字(即 0-999)
(?: # 尝试匹配...
\.# 一个小数点
[0-9]{1,10} # 后跟 1 到 10 位数字(即 0-9999999999)
)? # ...可选
$ # 字符串结束
【讨论】:
试试这个:
^(\()([-+]?)([\d]{1,2})(((\.)(\d+)(,)))(\s*)(([-+]?)([\d]{1,3})((\.)(\d+))?(\)))$
查看:
将表达式粘贴到顶部框中,然后将类似这样的内容放入底部框中:
(80.0123, -34.034)
(80.0123)
(80.a)
(980.13, 40)
(99.000, 122.000)
正则表达式分解:
^ # The string must start this way (there can't be anything before).
(\() # An opening parentheses (escaped with a backslash).
([-+]?) # An optional minus, or an optional plus.
([\d]{1,2}) # 1 or 2 digits (0-9).
( # Start of a sub-pattern.
( # Start of a sub-pattern.
(\.) # A dot (escaped with a backslash).
(\d+) # One or more digits (0-9).
(,) # A comma.
) # End of a sub-pattern.
) # End of a sub-pattern.
(\s*) # Zero or more spaces.
( # Start of a sub-pattern.
([-+]?) # An optional minus, or an optional plus.
([\d]{1,3}) # 1 to 3 digits (0-9).
( # Start of a pattern.
(\.) # A dot (escaped with a backslash).
(\d+) # One or more digits (0-9).
)? # End of an optional pattern.
(\)) # A closing parenthesis (escaped with a backkslash).
) # End of a pattern
$ # The string must end this way (there can't be anything after).
现在,它不做的是将自己限制在这个范围内:
(-90 to +90, and -180 to +180)
相反,它只是将自己限制在这个范围内:
(-99 to +99, -199 to +199)
但重点主要是分解每个表达式。
【讨论】:
@macro-ferrari我确实找到了一种缩短它的方法,并且根据最近关于regex engines的所有讨论,我没有向前看
const LAT_RE = /^[+-]?(([1-8]?[0-9])(\.[0-9]{1,6})?|90(\.0{1,6})?)$/;
const LONG_RE = /^[+-]?((([1-9]?[0-9]|1[0-7][0-9])(\.[0-9]{1,6})?)|180(\.0{1,6})?)$/;
【讨论】:
这是一个更严格的版本:
^([-+]?\d{1,2}[.]\d+),\s*([-+]?\d{1,3}[.]\d+)$
-90 -- +90
-180 -- +180
【讨论】:
Python:
纬度:result = re.match("^[+-]?((90\.?0*$)|(([0-8]?[0-9])\.?[0-9]*$))", '-90.00001')
经度:result = re.match("^[+-]?((180\.?0*$)|(((1[0-7][0-9])|([0-9]{0,2}))\.?[0-9]*$))", '-0.0000')
示例中的纬度应该失败。
【讨论】:
正则表达式通过将 [0-9] 的多次使用替换为 [0-9] 的子集来缩短 @marco-ferrari 解决方案。还从各个地方删除了不必要的量词,例如 ?:
lat "^([+-])?(?:90(?:\\.0{1,6})?|((?:|[1-8])[0-9])(?:\\.[0-9]{1,6})?)$";
long "^([+-])?(?:180(?:\\.0{1,6})?|((?:|[1-9]|1[0-7])[0-9])(?:\\.[0-9]{1,6})?)$";
**Matches for Lat**
Valid between -90 to +90 with up to 6 decimals.
**Matches for Long**
Valid between -180 to +180 with up to 6 decimals.
【讨论】:
我相信您使用的是 \w(单词字符),而您应该使用的是 \s(空格)。单词字符通常由 [A-Za-z0-9_] 组成,因此排除了您的空格,然后进一步无法匹配可选的减号或数字。
【讨论】:
这适用于如下格式:31 ͦ 37.4' E
^[-]?\d{1,2}[ ]*ͦ[ ]*\d{1,2}\.?\d{1,2}[ ]*\x27[ ]*\w$
【讨论】:
红宝石
经度-179.99999999..180
/^(-?(?:1[0-7]|[1-9])?\d(?:\.\d{1,8})?|180(?:\.0{1,8})?)$/ === longitude.to_s
纬度-89.99999999..90
/^(-?[1-8]?\d(?:\.\d{1,8})?|90(?:\.0{1,8})?)$/ === latitude.to_s
【讨论】:
Objective C 中用于检查经纬度正确模式的完整而简单的方法是:
-( BOOL )textIsValidValue:(NSString*) searchedString
{
NSRange searchedRange = NSMakeRange(0, [searchedString length]);
NSError *error = nil;
NSString *pattern = @"^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?),\\s*[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$";
NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern: pattern options:0 error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:searchedString options:0 range: searchedRange];
return match ? YES : NO;
}
其中 searchedString 是用户将在相应文本字段中输入的输入。
【讨论】:
这是 PHP 的版本(输入值为:$latitude 和 $longitude):
$latitude_pattern = '/\A[+-]?(?:90(?:\.0{1,18})?|\d(?(?<=9)|\d?)\.\d{1,18})\z/x';
$longitude_pattern = '/\A[+-]?(?:180(?:\.0{1,18})?|(?:1[0-7]\d|\d{1,2})\.\d{1,18})\z/x';
if (preg_match($latitude_pattern, $latitude) && preg_match($longitude_pattern, $longitude)) {
// Valid coordinates.
}
【讨论】:
这个在逗号后强制使用 3 个数字以避免错误匹配:
(?<latitude>-?\d+\.\d{3,10}),(?<longitude>-?\d+\.\d{3,10})
【讨论】:
你可以试试这个:
var latExp = /^(?=.)-?((8[0-5]?)|([0-7]?[0-9]))?(?:\.[0-9]{1,20})?$/;
var lngExp = /^(?=.)-?((0?[8-9][0-9])|180|([0-1]?[0-7]?[0-9]))?(?:\.[0-9]{1,20})?$/;
【讨论】:
试试这个:
^[-+]?(([0-8]\\d|\\d)(\\.\\d+)?|90(\\.0+)?)$,\s*^[-+]?((1[0-7]\\d(\\.\\d+)?)|(180(\\.0+)?)|(\\d\\d(\\.\\d+)?)|(\\d(\\.\\d+)?))$
【讨论】:
试试这个:
(?<!\d)([-+]?(?:[1-8]?\d(?:\.\d+)?|90(?:\.0+)?)),\s*([-+]?(?:180(?:\.0+)?|(?:(?:1[0-7]\d)|(?:[1-9]?\d))(?:\.\d+)?))(?!\d)`
【讨论】: