줄 안에 토큰의 위치를 가져올 수 없으며이 문제를 해결할 수있는 확실한 방법이 없습니다. 그러나 캡슐화 된 패턴 일치가 어쨌든 매우 발전되지 않았으므로 StreamTokenizer
을 대체 할 것을 고려할 수 있습니다. 장래에 다른 결점을 발견하게 될 수도 있습니다. 해결 방법이 될 수 없으며 패턴을 관리 할 때 더 쉽게 할 수 있습니다. 나는 바퀴를 재발 명에 대해 얘기하지만, 대신에 정규 표현식을 사용하지 않는 : parseStreamTokenizer
및 parseRegex
는, (내가 그들 자신의 소스 코드를 분석하자) parseRegex
인지되는 유일한 차이점을 동일한 결과를 생성
public static void parseStreamTokenizer(String filename) throws IOException {
try(Reader r=new FileReader(filename);
BufferedReader dataReader = new BufferedReader(r);) {
StreamTokenizer st=new StreamTokenizer(dataReader);
for(;;) {
double d=Double.NaN;
String w=null;
switch(st.nextToken()) {
case StreamTokenizer.TT_EOF: return;
case StreamTokenizer.TT_EOL: continue;
case StreamTokenizer.TT_NUMBER: d=st.nval; break;
case StreamTokenizer.TT_WORD: case '"': case '\'': w=st.sval; break;
}
consumeToken(st.lineno(), -1, st.ttype, w, d);
}
}
}
static final Pattern ALL_TOKENS = Pattern.compile(
"(-?(?:[0-9]+\\.?[0-9]*|\\.[0-9]*))" // number
+"|([A-Za-z][A-Za-z0-9\\.\\-]*)" // word
+"|([\"'])((?:\\\\?.)*?)\\3" // string with backslash escape
+"|/.*" // StreamTokenizer's "comment char" behavior
+"|\\s*" // white-space
);
public static void parseRegex(String filename) throws IOException {
try(Reader r=new FileReader(filename);
BufferedReader dataReader = new BufferedReader(r)) {
String line;
int lineNo=0;
Matcher m=ALL_TOKENS.matcher("");
while((line=dataReader.readLine())!=null) {
lineNo++;
m.reset(line);
int last=0;
while(m.find()) {
double d=Double.NaN;
String word=null;
for(int e=m.start(); last<e; last++) {
consumeToken(lineNo, last+1, line.charAt(last), word, d);
}
last=m.end();
int type;
if(m.start(1)>=0) {
type=StreamTokenizer.TT_NUMBER;
String n=m.group();
d=n.equals(".")? 0: Double.parseDouble(m.group());
}
else if(m.start(2)>=0) {
type=StreamTokenizer.TT_WORD;
word=m.group(2);
}
else if(m.start(4)>=0) {
type=line.charAt(m.start(3));
word=parse(line, m.start(4), m.end(4));
}
else continue;
consumeToken(lineNo, m.start()+1, type, word, d);
}
}
}
}
// the most complicated thing is interpreting escape sequences within strings
private static String parse(String source, int start, int end) {
for(int pos=start; pos<end; pos++) {
if(source.charAt(pos)=='\\') {
StringBuilder sb=new StringBuilder(end-start+16);
sb.append(source, start, pos);
for(; pos<end; pos++) {
if(source.charAt(pos)=='\\') {
int oct=0;
switch(source.charAt(++pos)) {
case 'n': sb.append('\n'); continue;
case 'r': sb.append('\r'); continue;
case 't': sb.append('\t'); continue;
case 'b': sb.append('\b'); continue;
case 'f': sb.append('\f'); continue;
case 'v': sb.append('\13'); continue;
case 'a': sb.append('\7'); continue;
case '0': case '1': case '2': case '3':
int next=pos+1;
if(next<end && (source.charAt(next)&~'7')==0)
oct=source.charAt(pos++)-'0';
// intentionally no break
case '4': case '5': case '6': case '7':
oct=oct*8+source.charAt(pos)-'0';
next=pos+1;
if(next<end && (source.charAt(next)&~'7')==0)
oct=oct*8+source.charAt(pos=next)-'0';
sb.append((char)oct);
continue;
}
}
sb.append(source.charAt(pos));
}
return sb.toString();
}
}
return source.substring(start, end);
}
// called from both variants, to the same result (besides col values)
static void consumeToken(int line, int col, int id, String word, double number) {
String type;
Object o;
switch(id)
{
case StreamTokenizer.TT_NUMBER: type="number"; o=number; break;
case StreamTokenizer.TT_WORD: type="word"; o=word; break;
case '"': case '\'': type="string"; o=word; break;
default: type="char"; o=(char)id;
}
System.out.printf("l %3d, c %3s: token %-6s %s%n",
line, col<0? "???": col, type, o);
}
하는 것으로 열 번호, 즉 줄 내의 위치를 제공 할 수 있습니다.
실제로 코드를 복잡하게 만드는 이유는 실제 사용 사례에 대해 자세히 지정하지 않았기 때문에 StreamTokenizer
과 동일한 결과를 재현하려는 시도입니다. 실제로 \v
및 \a
과 같은 비표준 이스케이프 시퀀스가 필요한지 또는 문자열에서 8 진수 이스케이프가 필요한지, 아니면 단일 점이 0.0
으로 해석되도록할지 또는 모든 숫자를 double
값으로 제공할지 여부는 알 수 없습니다. StreamTokenizer
입니다.
하지만 모든 실제 사용 사례의 경우 구문 분석기는 조만간 StreamTokenizer
(열 번호 초과)을 초과하는 기능을 요구하므로 더 복잡한 코드를 피할 수없는 방식으로 사용하게됩니다. 다른 한편, 그것은 또한 당신에게 더 많은 통제를 제공하고 불필요한 것들을 제거 할 수 있도록 위의 코드는 좋은 출발점을 제공해야합니다 ...
정보를 주셔서 감사합니다, 스티븐. 입력 파일을 토큰 화하고 구문 분석 오류의 오프셋을 정확하게 기록 할 수있게하는 것이 좋습니다. 나는 파일을 줄 단위로 읽는 것을 고려하고 있었지만, 줄 끝은 특별한 의미가 없다는 것이 문제였다. 그리고 나는 상향식 "예측"파서를 사용하고 있기 때문에 특정 토큰/세트를 읽을 수 있어야한다. 토큰을 덩어리로 만들었는데, 마치 라인 엔딩을 다루기가 더 어려워 보였습니다. – Paul