Java에서 "glob"유형 일치를 수행하기위한 표준 (가급적 Apache Commons 또는 유사하게 비 바이러스 성) 라이브러리가 있습니까? Perl에서 비슷한 작업을 한 번 수행해야했을 때 " .
"를 " \.
"로, " *
"를 " .*
"로, " ?
"를 " .
"로 변경했습니다.하지만 누군가이 작업을 수행했는지 궁금합니다. 나를 위해 일하십시오.
비슷한 질문 : glob 표현식에서 정규식 만들기
Java에서 "glob"유형 일치를 수행하기위한 표준 (가급적 Apache Commons 또는 유사하게 비 바이러스 성) 라이브러리가 있습니까? Perl에서 비슷한 작업을 한 번 수행해야했을 때 " .
"를 " \.
"로, " *
"를 " .*
"로, " ?
"를 " .
"로 변경했습니다.하지만 누군가이 작업을 수행했는지 궁금합니다. 나를 위해 일하십시오.
비슷한 질문 : glob 표현식에서 정규식 만들기
답변:
내장 된 것은 없지만 glob과 같은 것을 정규식으로 변환하는 것은 매우 간단합니다.
public static String createRegexFromGlob(String glob)
{
String out = "^";
for(int i = 0; i < glob.length(); ++i)
{
final char c = glob.charAt(i);
switch(c)
{
case '*': out += ".*"; break;
case '?': out += '.'; break;
case '.': out += "\\."; break;
case '\\': out += "\\\\"; break;
default: out += c;
}
}
out += '$';
return out;
}
이것은 나를 위해 작동하지만 글롭 "표준"을 포함하는지 확실하지 않습니다.
Paul Tomblin의 업데이트 : glob 변환을 수행하는 펄 프로그램을 발견하고이를 Java에 적용하면 다음과 같이 끝납니다.
private String convertGlobToRegEx(String line)
{
LOG.info("got line [" + line + "]");
line = line.trim();
int strLen = line.length();
StringBuilder sb = new StringBuilder(strLen);
// Remove beginning and ending * globs because they're useless
if (line.startsWith("*"))
{
line = line.substring(1);
strLen--;
}
if (line.endsWith("*"))
{
line = line.substring(0, strLen-1);
strLen--;
}
boolean escaping = false;
int inCurlies = 0;
for (char currentChar : line.toCharArray())
{
switch (currentChar)
{
case '*':
if (escaping)
sb.append("\\*");
else
sb.append(".*");
escaping = false;
break;
case '?':
if (escaping)
sb.append("\\?");
else
sb.append('.');
escaping = false;
break;
case '.':
case '(':
case ')':
case '+':
case '|':
case '^':
case '$':
case '@':
case '%':
sb.append('\\');
sb.append(currentChar);
escaping = false;
break;
case '\\':
if (escaping)
{
sb.append("\\\\");
escaping = false;
}
else
escaping = true;
break;
case '{':
if (escaping)
{
sb.append("\\{");
}
else
{
sb.append('(');
inCurlies++;
}
escaping = false;
break;
case '}':
if (inCurlies > 0 && !escaping)
{
sb.append(')');
inCurlies--;
}
else if (escaping)
sb.append("\\}");
else
sb.append("}");
escaping = false;
break;
case ',':
if (inCurlies > 0 && !escaping)
{
sb.append('|');
}
else if (escaping)
sb.append("\\,");
else
sb.append(",");
break;
default:
escaping = false;
sb.append(currentChar);
}
}
return sb.toString();
}
이 답변이 나를 올바른 길로 인도하기 때문에 내 답변을 만들기보다는이 답변을 편집하고 있습니다.
Globbing은 Java 7 에서도 구현 될 예정입니다 .
기여해 주신 모든 분들께 감사드립니다. 이전 답변보다 더 포괄적 인 변환을 작성했습니다.
/**
* Converts a standard POSIX Shell globbing pattern into a regular expression
* pattern. The result can be used with the standard {@link java.util.regex} API to
* recognize strings which match the glob pattern.
* <p/>
* See also, the POSIX Shell language:
* http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13_01
*
* @param pattern A glob pattern.
* @return A regex pattern to recognize the given glob pattern.
*/
public static final String convertGlobToRegex(String pattern) {
StringBuilder sb = new StringBuilder(pattern.length());
int inGroup = 0;
int inClass = 0;
int firstIndexInClass = -1;
char[] arr = pattern.toCharArray();
for (int i = 0; i < arr.length; i++) {
char ch = arr[i];
switch (ch) {
case '\\':
if (++i >= arr.length) {
sb.append('\\');
} else {
char next = arr[i];
switch (next) {
case ',':
// escape not needed
break;
case 'Q':
case 'E':
// extra escape needed
sb.append('\\');
default:
sb.append('\\');
}
sb.append(next);
}
break;
case '*':
if (inClass == 0)
sb.append(".*");
else
sb.append('*');
break;
case '?':
if (inClass == 0)
sb.append('.');
else
sb.append('?');
break;
case '[':
inClass++;
firstIndexInClass = i+1;
sb.append('[');
break;
case ']':
inClass--;
sb.append(']');
break;
case '.':
case '(':
case ')':
case '+':
case '|':
case '^':
case '$':
case '@':
case '%':
if (inClass == 0 || (firstIndexInClass == i && ch == '^'))
sb.append('\\');
sb.append(ch);
break;
case '!':
if (firstIndexInClass == i)
sb.append('^');
else
sb.append('!');
break;
case '{':
inGroup++;
sb.append('(');
break;
case '}':
inGroup--;
sb.append(')');
break;
case ',':
if (inGroup > 0)
sb.append('|');
else
sb.append(',');
break;
default:
sb.append(ch);
}
}
return sb.toString();
}
그리고 그것이 작동하는지 증명하기 위해 단위 테스트 :
/**
* @author Neil Traft
*/
public class StringUtils_ConvertGlobToRegex_Test {
@Test
public void star_becomes_dot_star() throws Exception {
assertEquals("gl.*b", StringUtils.convertGlobToRegex("gl*b"));
}
@Test
public void escaped_star_is_unchanged() throws Exception {
assertEquals("gl\\*b", StringUtils.convertGlobToRegex("gl\\*b"));
}
@Test
public void question_mark_becomes_dot() throws Exception {
assertEquals("gl.b", StringUtils.convertGlobToRegex("gl?b"));
}
@Test
public void escaped_question_mark_is_unchanged() throws Exception {
assertEquals("gl\\?b", StringUtils.convertGlobToRegex("gl\\?b"));
}
@Test
public void character_classes_dont_need_conversion() throws Exception {
assertEquals("gl[-o]b", StringUtils.convertGlobToRegex("gl[-o]b"));
}
@Test
public void escaped_classes_are_unchanged() throws Exception {
assertEquals("gl\\[-o\\]b", StringUtils.convertGlobToRegex("gl\\[-o\\]b"));
}
@Test
public void negation_in_character_classes() throws Exception {
assertEquals("gl[^a-n!p-z]b", StringUtils.convertGlobToRegex("gl[!a-n!p-z]b"));
}
@Test
public void nested_negation_in_character_classes() throws Exception {
assertEquals("gl[[^a-n]!p-z]b", StringUtils.convertGlobToRegex("gl[[!a-n]!p-z]b"));
}
@Test
public void escape_carat_if_it_is_the_first_char_in_a_character_class() throws Exception {
assertEquals("gl[\\^o]b", StringUtils.convertGlobToRegex("gl[^o]b"));
}
@Test
public void metachars_are_escaped() throws Exception {
assertEquals("gl..*\\.\\(\\)\\+\\|\\^\\$\\@\\%b", StringUtils.convertGlobToRegex("gl?*.()+|^$@%b"));
}
@Test
public void metachars_in_character_classes_dont_need_escaping() throws Exception {
assertEquals("gl[?*.()+|^$@%]b", StringUtils.convertGlobToRegex("gl[?*.()+|^$@%]b"));
}
@Test
public void escaped_backslash_is_unchanged() throws Exception {
assertEquals("gl\\\\b", StringUtils.convertGlobToRegex("gl\\\\b"));
}
@Test
public void slashQ_and_slashE_are_escaped() throws Exception {
assertEquals("\\\\Qglob\\\\E", StringUtils.convertGlobToRegex("\\Qglob\\E"));
}
@Test
public void braces_are_turned_into_groups() throws Exception {
assertEquals("(glob|regex)", StringUtils.convertGlobToRegex("{glob,regex}"));
}
@Test
public void escaped_braces_are_unchanged() throws Exception {
assertEquals("\\{glob\\}", StringUtils.convertGlobToRegex("\\{glob\\}"));
}
@Test
public void commas_dont_need_escaping() throws Exception {
assertEquals("(glob,regex),", StringUtils.convertGlobToRegex("{glob\\,regex},"));
}
}
나열된 것보다 더 현대적인 Glob 유사 패턴 일치를 수행하는 라이브러리가 몇 개 있습니다.
Theres Ants 디렉토리 스캐너 및 Springs AntPathMatcher
나는 다른 솔루션보다 둘 다 권장합니다. Ant Style Globbing이 Java 세계 (Hudson, Spring, Ant 및 Maven) 에서 표준 glob 구문이 .
나는 최근에 그것을 사용 \Q
하고 \E
glob 패턴을 탈출 해야했습니다 .
private static Pattern getPatternFromGlob(String glob) {
return Pattern.compile(
"^" + Pattern.quote(glob)
.replace("*", "\\E.*\\Q")
.replace("?", "\\E.\\Q")
+ "$");
}
glob
glob = Pattern.quote (glob)로 변수를 사전 처리하여이를 피할 수 있습니다 . 그러나이 경우 첫 번째와 마지막 \\ Q 및 \\ E를 앞에 추가하고 추가 할 필요가 없습니다.
이것은 * 및?를 처리하는 간단한 Glob 구현입니다. 패턴에서
public class GlobMatch {
private String text;
private String pattern;
public boolean match(String text, String pattern) {
this.text = text;
this.pattern = pattern;
return matchCharacter(0, 0);
}
private boolean matchCharacter(int patternIndex, int textIndex) {
if (patternIndex >= pattern.length()) {
return false;
}
switch(pattern.charAt(patternIndex)) {
case '?':
// Match any character
if (textIndex >= text.length()) {
return false;
}
break;
case '*':
// * at the end of the pattern will match anything
if (patternIndex + 1 >= pattern.length() || textIndex >= text.length()) {
return true;
}
// Probe forward to see if we can get a match
while (textIndex < text.length()) {
if (matchCharacter(patternIndex + 1, textIndex)) {
return true;
}
textIndex++;
}
return false;
default:
if (textIndex >= text.length()) {
return false;
}
String textChar = text.substring(textIndex, textIndex + 1);
String patternChar = pattern.substring(patternIndex, patternIndex + 1);
// Note the match is case insensitive
if (textChar.compareToIgnoreCase(patternChar) != 0) {
return false;
}
}
// End of pattern and text?
if (patternIndex + 1 >= pattern.length() && textIndex + 1 >= text.length()) {
return true;
}
// Go on to match the next character in the pattern
return matchCharacter(patternIndex + 1, textIndex + 1);
}
}
Tony Edgecombe 의 답변 과 유사하게 누구나 필요하다면 정규식 을 지원 *
하고 ?
사용하지 않는 짧고 간단한 글로버가 있습니다.
public static boolean matches(String text, String glob) {
String rest = null;
int pos = glob.indexOf('*');
if (pos != -1) {
rest = glob.substring(pos + 1);
glob = glob.substring(0, pos);
}
if (glob.length() > text.length())
return false;
// handle the part up to the first *
for (int i = 0; i < glob.length(); i++)
if (glob.charAt(i) != '?'
&& !glob.substring(i, i + 1).equalsIgnoreCase(text.substring(i, i + 1)))
return false;
// recurse for the part after the first *, if any
if (rest == null) {
return glob.length() == text.length();
} else {
for (int i = glob.length(); i <= text.length(); i++) {
if (matches(text.substring(i), rest))
return true;
}
return false;
}
}
약간 해키 한 접근 방식 일 수 있습니다. 나는 NIO2의 Files.newDirectoryStream(Path dir, String glob)
코드 에서 그것을 알아 냈다 . 모든 일치하는 새 Path
개체가 생성 된다는 점에주의하십시오 . 지금까지는 Windows FS에서만 테스트 할 수 있었지만 Unix에서도 작동해야한다고 생각합니다.
// a file system hack to get a glob matching
PathMatcher matcher = ("*".equals(glob)) ? null
: FileSystems.getDefault().getPathMatcher("glob:" + glob);
if ("*".equals(glob) || matcher.matches(Paths.get(someName))) {
// do you stuff here
}
업데이트는 Mac과 Linux 모두에서 작동합니다.
"표준"구현에 대해서는 모르지만 파일에 대한 glob 매칭을 구현 한 BSD 라이선스에 따라 릴리스 된 sourceforge 프로젝트에 대해 알고 있습니다. 하나의 파일로 구현되며 요구 사항에 맞게 조정할 수 있습니다.
오래 전에 저는 대규모 글롭 기반 텍스트 필터링을 수행하고 있었기 때문에 작은 코드 조각을 작성했습니다 (코드 15 줄, JDK 이상의 종속성 없음). '*'만 처리하지만 (나에게 충분 함) '?'에 대해서는 쉽게 확장 할 수 있습니다. 사전 컴파일 된 정규 표현식보다 몇 배 빠르며 사전 컴파일이 필요하지 않습니다 (본질적으로 패턴이 일치 할 때마다 문자열 대 문자열 비교).
암호:
public static boolean miniglob(String[] pattern, String line) {
if (pattern.length == 0) return line.isEmpty();
else if (pattern.length == 1) return line.equals(pattern[0]);
else {
if (!line.startsWith(pattern[0])) return false;
int idx = pattern[0].length();
for (int i = 1; i < pattern.length - 1; ++i) {
String patternTok = pattern[i];
int nextIdx = line.indexOf(patternTok, idx);
if (nextIdx < 0) return false;
else idx = nextIdx + patternTok.length();
}
if (!line.endsWith(pattern[pattern.length - 1])) return false;
return true;
}
}
용법:
public static void main(String[] args) {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
try {
// read from stdin space separated text and pattern
for (String input = in.readLine(); input != null; input = in.readLine()) {
String[] tokens = input.split(" ");
String line = tokens[0];
String[] pattern = tokens[1].split("\\*+", -1 /* want empty trailing token if any */);
// check matcher performance
long tm0 = System.currentTimeMillis();
for (int i = 0; i < 1000000; ++i) {
miniglob(pattern, line);
}
long tm1 = System.currentTimeMillis();
System.out.println("miniglob took " + (tm1-tm0) + " ms");
// check regexp performance
Pattern reptn = Pattern.compile(tokens[1].replace("*", ".*"));
Matcher mtchr = reptn.matcher(line);
tm0 = System.currentTimeMillis();
for (int i = 0; i < 1000000; ++i) {
mtchr.matches();
}
tm1 = System.currentTimeMillis();
System.out.println("regexp took " + (tm1-tm0) + " ms");
// check if miniglob worked correctly
if (miniglob(pattern, line)) {
System.out.println("+ >" + line);
}
else {
System.out.println("- >" + line);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
여기 에서 복사 / 붙여 넣기
Vincent Robert / dimo414 의 이전 솔루션 은 API에 문서화되지 않았으므로 다른 / 향후 Java 구현의 경우가 아닐 수있는 ... Pattern.quote()
측면에서 구현에 의존합니다 . 다음 솔루션은를 사용 하는 대신 모든 발생을 이스케이프하여 구현 종속성을 제거합니다 . 일치시킬 문자열에 줄 바꿈이 포함 된 경우 모드 ( ) 도 활성화 됩니다 .\Q
\E
\E
quote()
DOTALL
(?s)
public static Pattern globToRegex(String glob)
{
return Pattern.compile(
"(?s)^\\Q" +
glob.replace("\\E", "\\E\\\\E\\Q")
.replace("*", "\\E.*\\Q")
.replace("?", "\\E.\\Q") +
"\\E$"
);
}
그건 그렇고, Perl에서 힘들게 한 것처럼 보입니다.
이것은 Perl에서 트릭을 수행합니다.
my @files = glob("*.html")
# Or, if you prefer:
my @files = <*.html>