[英]Reversing a regex based parser
我繼承了一個銀行接口解析器。 以前的開發人員實際上做到了這一點。 來自庫的文件是固定長度字段。 他從下載中解析記錄的方式是這樣的
public static final String HEADER_RECORD_REGEX = "^(\\d{3})(\\d{12})(.{20})(\\d\\d)(\\d\\d)(\\d\\d)(\\d{12})(\\d\\d)$";
private static final int BANK_ID = 1;
private static final int ACCOUNT_ID = 2;
private static final int COMPANY_NAME = 3;
private static final int MONTH = 4;
private static final int DAY = 5;
private static final int YEAR = 6;
private static final int SEQUENCE = 7;
private static final int TYPE_CODE = 8;
private static final int GROUP_COUNT = TYPE_CODE;
if ( GROUP_COUNT == matcher.groupCount() ) {
setBankId( matcher.group( BANK_ID ) );
setAccountId( matcher.group( ACCOUNT_ID ) );
setCompanyName( matcher.group( COMPANY_NAME ) );
setProcessDate( matcher.group( MONTH ), matcher.group( DAY ),
matcher.group( YEAR ) );
setSeqNumber( matcher.group( SEQUENCE ) );
setTypeCode( matcher.group( TYPE_CODE ) );
}
我有一個新要求,要求撤銷此過程,並從銀行實際生成模擬文件,以便我們進行測試。 使用這種方法,有沒有一種方法可以使我使用相同的正則表達式方法來逆轉該過程以生成文件,或者我只是回到構建標准解析器。
謝謝
這基本上可以滿足您的要求。 您可以使用它直到適合您的需求。
import java.util.*;
class Main
{
public static String getLine(String bankID, String acctID, String companyName, String month, String day, String year, String seq, String typeCode)
{
return new Formatter()
.format("%3.3s%12.12s%20.20s%2.2s%2.2s%2.2s%12.12s%2.2s",
bankID, acctID, companyName, month,
day, year, seq, typeCode)
.toString(); // 1 semicolon, technically a 1 liner. aww yeah
}
public static void main(String[] args)
{
String tester = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
System.out.println(getLine(tester, tester, tester, tester,
tester, tester, tester, tester));
}
}
該示例的輸出為:
123123456789ABC123456789ABCDEFGHIJK121212123456789ABC12
如果反向意味着將對象輸出到文件,則解析器不是您所需要的。 您需要做的就是實現一種方法,該方法使用與文件相似的格式輸出相同的數據成員。 您可以將String.format與正則表達式中的字段長度一起使用。 通過一些重構,您可以提取正則表達式和字符串格式之間的共性,盡管您可能認為這是一個過大的選擇,因為此正則表達式非常簡單。
您需要遠離讓正則表達式控制您的步驟。 如果您以另一種方式定義結構(在下面我使用一個enum
),您可以從中獲取正則表達式和格式器,那么代碼不僅將變得可擴展得多,而且還可以從中進行編組和拆組太。
這樣的事情可能是一個好的開始:
public class BankRecords {
static enum AccountField {
BANK_ID("\\d", 3) {
@Override
void fill ( Account a, String s ) {
a.bankId = s;
}
},
ACCOUNT_ID("\\d", 12) {
@Override
void fill ( Account a, String s ) {
a.accountID = s;
}
},
COMPANY_NAME(".", 20) {
@Override
void fill ( Account a, String s ) {
a.companyName = s;
}
},
MONTH("\\d", 2) {
@Override
void fill ( Account a, String s ) {
a.month = s;
}
},
DAY("\\d", 2) {
@Override
void fill ( Account a, String s ) {
a.day = s;
}
},
YEAR("\\d", 2) {
@Override
void fill ( Account a, String s ) {
a.year = s;
}
},
SEQUENCE("\\d", 12) {
@Override
void fill ( Account a, String s ) {
a.seqNumber = s;
}
},
TYPE_CODE("\\d", 2) {
@Override
void fill ( Account a, String s ) {
a.typeCode = s;
}
};
// The type string in the regex.
final String type;
// How many characters.
final int count;
AccountField(String type, int count) {
this.type = type;
this.count = count;
}
// Each field can fill its part in the Account.
abstract void fill ( Account a, String s );
// My pattern.
static Pattern pattern = Pattern.compile(asRegex());
public static Account parse ( String record ) {
Account account = new Account ();
// Fire off the matcher with the regex and put each field in the Account object.
Matcher matcher = pattern.matcher(record);
for ( AccountField f : AccountField.values() ) {
f.fill(account, matcher.group(f.ordinal() + 1));
}
return account;
}
public static String format ( Account account ) {
StringBuilder s = new StringBuilder ();
// Roll each field of the account into the string using the correct length from the enum.
return s.toString();
}
private static String regex = null;
static String asRegex() {
// Only do this once.
if (regex == null) {
// Grow my regex from the field definitions.
StringBuilder r = new StringBuilder("^");
for (AccountField f : AccountField.values()) {
r.append("(").append(f.type);
// Special case count = 1 or 2.
switch (f.count) {
case 1:
break;
case 2:
// Just one more.
r.append(f.type);
break;
default:
// More than that shoudl use the {} notation.
r.append("{").append(f.count).append("}");
break;
}
r.append(")");
}
// End of record.
r.append("$");
regex = r.toString();
}
return regex;
}
}
public static class Account {
String bankId;
String accountID;
String companyName;
String month;
String day;
String year;
String seqNumber;
String typeCode;
}
}
注意每個enum
如何封裝每個字段的本質。 類型,字符數及其在Account
對象中的位置。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.