【发布时间】:2021-03-04 06:09:06
【问题描述】:
我想在所有具有两个以上类型参数的子目录中递归地打印 *.java 文件的标题(即下面示例中 <R ... H> 中的参数)。其中一个文件看起来像(为简洁起见减少了名称):
multiple-lines.java
class ClazzA<R extends A,
S extends B<T>, T extends C<T>,
U extends D, W extends E,
X extends F, Y extends G, Z extends H>
extends OtherClazz<S> implements I<T> {
public void method(Type<Q, R> x) {
// ... code ...
}
}
预期输出:
ClazzA.java:10: class ClazzA<R extends A,
ClazzA.java:11: S extends B<T>, T extends C<T>,
ClazzA.java:12: U extends D, W extends E,
ClazzA.java:13: X extends F, Y extends G, Z extends H>
ClazzA.java:14: extends OtherClazz<S> implements I<T> {
但另一个也可能是这样的:
single-line.java
class ClazzB<R extends A, S extends B<T>, T extends C<T>, U extends D, W extends E, X extends F, Y extends G, Z extends H> extends OtherClazz<S> implements I<T> {
public void method(Type<Q, R> x) {
// ... code ...
}
}
预期输出:
ClazzB.java:42: class ClazzB<R extends A, S extends B<T>, T extends C<T>, U extends D, W extends E, X extends F, Y extends G, Z extends H> extends OtherClazz<S> implements I<T> {
不应考虑/打印的文件:
X-no-parameter.java
class ClazzC /* no type parameter */ extends OtherClazz<S> implements I<T> {
public void method(Type<A, B> x) {
// ... code ...
}
}
X-one-parameter.java
class ClazzD<R extends A> // only one type parameter
extends OtherClazz<S> implements I<T> {
public void method(Type<X, Y> x) {
// ... code ...
}
}
X-two-parameters.java
class ClazzE<R extends A, S extends B<T>> // only two type parameters
extends OtherClazz<S> implements I<T> {
public void method(Type<X, Y> x) {
// ... code ...
}
}
X-two-line-parameters.java
class ClazzF<R extends A, // only two type parameters
S extends B<T>> // on two lines
extends OtherClazz<S> implements I<T> {
public void method(Type<X, Y> x) {
// ... code ...
}
}
文件中的所有空格都可以是\s+。紧接在{ 之前的extends [...] 和implements [...] 是可选的。 extends [...] 在每个类型参数中也是可选的。详情请见The Java® Language Specification, 8.1. Class Declarations。
我在 Git Bash 中使用gawk:
$ gawk --version
GNU Awk 5.0.0, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.2.0)
与:
find . -type f -name '*.java' | xargs gawk -f ws-class-type-parameter.awk > ws-class-type-parameter.log
和ws-class-type-parameter.awk:
# /start/ , /end/ ... pattern
#/class ClazzA<.*,.*/ , /{/ { # 5 lines, OK for ClazzA, but in real it prints classes with 2 or less type parameters, too
#/class ClazzA<.*,.*,/ , /{/ { # no line with ClazzA, since there's no second ',' on its first line
#/class ClazzA<.*,.*,/s , /{/ { # 500.000+(!) lines
#/class ClazzA<.*,.*,/s , /{/U { # 500.000+(!) lines
#/class ClazzA<.*,.*,/sU , /{/U { # 500.000+(!) lines
/(?s)class ClazzA<.*,.*,/ , /{/ { # no line
match( FILENAME, "/.*/.." )
print substr( FILENAME, RLENGTH ) ":" FNR ": " $0
}
这会找到所有*.java 文件...很好,对每个文件执行gawk...很好,但是在我尝试之后您会看到结果为 cmets。请注意:ClazzA 文字仅用于测试,MCVE 在这里。实际可能是\w+,但在测试时,数千个文件中有 500.000 多行...
如果我在regex101.com 上尝试它会起作用。嗯,有点。我没有找到如何在那里定义/start-regex/,/end-regex/,所以我在两者之间添加了另一个.*。
我从那里拿到了标志,但我找不到gawk 是否支持标志语法/.../sU , /.../U 的描述,所以我试了一下。一条现已删除的评论告诉我,没有任何 awk 支持这一点。
我也试过grep:
$ grep --version
grep (GNU grep) 3.1
...
$ grep -nrPf types.grep *.java
使用 types.grep:
(?s).*class\s+\w+\s*<.*,.*,.*>.*{
这只会导致 singleline.java 的输出。
(?s) 是 --perl-regexp, -P 语法,grep --help 声称支持这一点。
更新
Ed Morton 的答案中的解决方案效果很好,但事实证明有自动生成的文件具有以下方法:
/** more code before here */
public void setId(String value) {
this.id = value;
}
/**
* Gets a map that contains attributes that aren't bound to any typed property on this class.
*
* <p>
* the map is keyed by the name of the attribute and
* the value is the string value of the attribute.
*
* the map returned by this method is live, and you can add new attribute
* by updating the map directly. Because of this design, there's no setter.
*
*
* @return
* always non-null
*/
public Map<QName, String> getOtherAttributes() {
return otherAttributes;
}
输出例如:
AbstractAddressType.java:81: * Gets a map that contains attributes that aren't bound to any typed property on this class.
AbstractAddressType.java:82: *
AbstractAddressType.java:83: * <p>
AbstractAddressType.java:84: * the map is keyed by the name of the attribute and
AbstractAddressType.java:85: * the value is the string value of the attribute.
AbstractAddressType.java:86: *
AbstractAddressType.java:87: * the map returned by this method is live, and you can add new attribute
AbstractAddressType.java:88: * by updating the map directly. Because of this design, there's no setter.
AbstractAddressType.java:89: *
AbstractAddressType.java:90: *
AbstractAddressType.java:91: * @return
AbstractAddressType.java:92: * always non-null
AbstractAddressType.java:93: */
AbstractAddressType.java:94: public Map<QName, String> getOtherAttributes() {
以及其他具有类 cmets 和注释的人,例如:
/**
* This class was generated by Apache CXF 3.3.4
* 2020-11-30T12:03:21.251+01:00
* Generated source version: 3.3.4
*
*/
@WebService(targetNamespace = "urn:SZRServices", name = "SZR")
@XmlSeeAlso({at.gv.egov.pvp1.ObjectFactory.class, org.w3._2001._04.xmldsig_more_.ObjectFactory.class, ObjectFactory.class, org.xmlsoap.schemas.ws._2002._04.secext.ObjectFactory.class, org.w3._2000._09.xmldsig_.ObjectFactory.class, at.gv.e_government.reference.namespace.persondata._20020228_.ObjectFactory.class})
public interface SZR {
// more code after here
输出例如:
SZR.java:13: * This class was generated by Apache CXF 3.3.4
SZR.java:14: * 2020-10-12T11:51:35.175+02:00
SZR.java:15: * Generated source version: 3.3.4
SZR.java:16: *
SZR.java:17: */
SZR.java:18: @WebService(targetNamespace = "urn:SZRServices", name = "SZR")
SZR.java:19: @XmlSeeAlso({at.gv.egov.pvp1.ObjectFactory.class, org.w3._2001._04.xmldsig_more_.ObjectFactory.class, ObjectFactory.class, org.xmlsoap.schemas.ws._2002._04.secext.ObjectFactory.class, org.w3._2000._09.xmldsig_.ObjectFactory.class, at.gv.e_government.reference.namespace.persondata._20020228_.ObjectFactory.class})
【问题讨论】:
-
如果我必须做这样的事情,我不会尝试单个正则表达式,我会用 perl 编写一个小型状态机。我的头顶有 3 个状态“寻找 ” - 这处理了将用单个正则表达式造成严重破坏的多行问题 :-)
-
@John3136 感谢您的评论。一条现已删除的评论也提议编写 Java 类解析器。您(或其他人,就此而言)能否至少确认它应该与 Perl 正则表达式
(?s)一起工作——这个想法是正确的吗? -
对于您尝试过的 grep 命令,您还需要
-z选项来匹配多行(我没有尝试完全理解这个问题,只是给出一个建议).. 但是,您还需要-o选项来仅显示匹配的部分,您不会在输出中获得行号,每次匹配结束时都会添加一个 ASCII NUL 字符,等等 -
如果你可以安装github.com/BurntSushi/ripgrep,你会得到更快的结果,你可以使用
-U选项进行多行匹配(不需要使用NUL作为输入行分隔符)以及行号输出等
标签: java regex awk grep git-bash