1

주제: replaceAll 버그(무한루프)

1.0.4 이후 개발버전(원본, 실험본 둘 다)에서 script/common.js 파일 내에 추가된 replaceAll 함수 문제입니다.
기존의 방식은 replaceAll("&", "&") 같은 경우 무한루프에 빠지게 됩니다.
(script/generaltag.js 파일 내에 존재함)

태그 내에 "te"를 입력하면 무한루프에 빠지길래 원인을 찾다가 발견하게 되었네요.
(Terms & Vocabulary 라는 태그에서 무한 루프에 빠지는 듯..)

if(!String.prototype.replaceAll) {
    String.prototype.replaceAll = function(source, target) {
        var buffer = this;
        if(source != target)
            while(buffer.indexOf(source) != -1)
                buffer = buffer.replace(source, target);
        return buffer;
    }
}

위를 아래와 같이 수정해서 실험본에 커밋하겠습니다.

if(!String.prototype.replaceAll) {
    String.prototype.replaceAll = function(source, target) {
        return this.replace(new RegExp(source, "gi"), target);
    }
}

2

답글: replaceAll 버그(무한루프)

근데 replaceAll 함수의 용도가 source 문자열에 정규식이 들어와도 영향받지 않고 말그대로 replaceAll 하는거라 수정하신 코드는 예외적인 경우가 발생할 수 있을 것 같습니다. (str="a|b";str=str.replaceAll("|", ":"); 같은 경우..)
으음.. 문자열 노가다를 최소화 하면서 replaceAll이 가능한 방법을 찾아봐야 될 것 같군요

3

답글: replaceAll 버그(무한루프)

indexOf에 startIndex를 주면 될듯. 변경된 부분 다음을 index로 넘겨주는 로직이 추가되면 될듯 하군요.

4

답글: replaceAll 버그(무한루프)

indexOf만 가지고 해결하는 방법은 잘 모르겠네요.. 무한루프는 막아질 수 있으나 "a&b&c".replaceAll("&", "&") 하면 a&b&c 이렇게 바뀌지 않을까요?

아무튼 스트링 노가다를 통한 방법은 아래처럼 가능할 것 같습니다

var StringBuffer = function()
{ this.buffer = new Array(); }

StringBuffer.prototype.append=function(str)
{ this.buffer[this.buffer.length] = str; }

StringBuffer.prototype.toString = function()
{ return this.buffer.join(""); }

if(!String.prototype.replaceAll) {
    String.prototype.replaceAll = function(source, target) {
        if(source.length == 0)
            return this;
        var sb = new StringBuffer();
        var clone = this;
        var position = clone.indexOf(source);
        while(position != -1) {
            sb.append(clone.substring(0, position));
            sb.append(target);
            clone = clone.substring(position + source.length, clone.length);
            position = clone.indexOf(source);
        }
        sb.append(clone);
        return sb.toString();
    }
}

단점이라면 겁나게 느리다는 것;

Peris님 코드처럼 정규식을 이용할 경우에는 source 문자열에서 정규식 표현부분을 다 제거해줘야 합니다

if(!String.prototype.replaceAllReg) {
    String.prototype.replaceAllReg = function(source, target) {
        var specialChars = "(\\\\|\\||\\.|\\*|\\+|\\?|\\{|\\}|\\[|\\]|\\(|\\)|\\^|\\$)";
        source = source.replace(new RegExp(specialChars, "g"), "\\$1");
        return this.replace(new RegExp(source, "gm"), target);
    }
}

이렇게 하면 대략 50배정도 빠르긴 한데 모든 경우를 체크해서 specialChars를 꼼꼼히 만들어줘야 합니다. 안그러면 시한폭탄!

의견 주세요..;

5

답글: replaceAll 버그(무한루프)

    if (source != target) {
        var startIndex = 0;
        var curIndex = 0;
        var sb = "";
        while ((curIndex = buffer.indexOf(source, startIndex)) != -1) {
            sb += buffer.substr(startIndex, curIndex - startIndex);
            sb += target;
            startIndex = curIndex + source.length;
        }
        
        sb += buffer.substr(startIndex, buffer.length - startIndex);
        
        buffer = sb;
    }

저의 첫 자바스크립트 코드입니다. -0- 적당히 고쳐서... 느리긴 하겠지만 그래도 심각하게는???

6

답글: replaceAll 버그(무한루프)

여러 책이나 글 등에서 regular expression이 느리다고 되도록 쓰지 말라고 하나,
script language에서는 직접 manipulate하는 것보다 re가 더 빠릅니다.
re로 구현하는 것에 한 표!

7

답글: replaceAll 버그(무한루프)

잠시 의문이.. JavaScript에서 Replace 함수를 찬찬히 생각해 보면 당연히 StartIndex를 받아야 할 것 같은데 왜 없을가요? --?
첫번째것만 처리해 줄꺼면 당연히 한방짜리나 시작위치 정해줄 수 있어야 하는거 아니겠음? (서울1945버젼)

8

답글: replaceAll 버그(무한루프)

음.. 제가 잘못 이해하고 있는건가요?

str="a|b";str=str.replaceAll("|", ":");

이런 경우 str=str.replaceAll("\\|", ":"); 이라고 쓰겠죠.


뭐 사실 저라면 replaceAll을 따로 만들지 않고 str=str.replace(/\|/gi, ":"); 이렇게 사용하고 말거 같네요.;;

9

답글: replaceAll 버그(무한루프)

gendoh 작성:

잠시 의문이.. JavaScript에서 Replace 함수를 찬찬히 생각해 보면 당연히 StartIndex를 받아야 할 것 같은데 왜 없을가요? --?
첫번째것만 처리해 줄꺼면 당연히 한방짜리나 시작위치 정해줄 수 있어야 하는거 아니겠음? (서울1945버젼)

정규표현식에서 index를 받아서 처리하는게 더 이상할 거 같네요.;;

10

답글: replaceAll 버그(무한루프)

specialChars 를 등록하는 방법은 아래와 같이하시면 됩니다.

php의 경우 non-word 문자에 대해서 그 문자 자체를 사용하기 위해서 \를 붙여주는 것이 항상 안전하다고 매뉴얼에 적혀 있습니다. smile
(js에서도 통용되는 말인지는 잘 모르겠지만..;; )

String.prototype.replaceAll = function(source, target) {
    source = source.replace(/(\W)/g, "\\$1");
    return this.replace(new RegExp(source, "gi"), target);
}

일단 이렇게 커밋해두겠습니다.

Peris (2006-04-24 19:13:33)에 의해 마지막으로 수정

11

답글: replaceAll 버그(무한루프)

그렇게 하면 간단하군요! smile

그리고 두번째 정규식에서 case insensitive 옵션은 빼고 multi line 옵션을 넣어서

if(!String.prototype.replaceAll) {
    String.prototype.replaceAll = function(source, target) {
        source = source.replace(new RegExp("(\\W)", "g"), "\\$1");
        return this.replace(new RegExp(source, "gm"), target);
    }
}

최종적으로 이렇게 했으면 합니다

코멘트 주세요~

crizin (2006-04-24 22:47:03)에 의해 마지막으로 수정

12

답글: replaceAll 버그(무한루프)

crizin 작성:

그렇게 하면 간단하군요! smile

그리고 두번째 정규식에서 case insensitive 옵션은 빼고 multi line 옵션을 넣어서

if(!String.prototype.replaceAll) {
    String.prototype.replaceAll = function(source, target) {
        source = source.replace(new RegExp("(\\W)", "g"), "\\$1");
        return this.replace(new RegExp(source, "gm"), target);
    }
}

최종적으로 이렇게 했으면 합니다

코멘트 주세요~

쓸데없는 말입니다만....이러한 thread 를 가만히 보고 있으면 과연 집단의 지혜는 정말 대단하구나...라는 생각을 하게 됩니다. smile

13

답글: replaceAll 버그(무한루프)

crizin 작성:

그렇게 하면 간단하군요! smile

그리고 두번째 정규식에서 case insensitive 옵션은 빼고 multi line 옵션을 넣어서

if(!String.prototype.replaceAll) {
    String.prototype.replaceAll = function(source, target) {
        source = source.replace(new RegExp("(\\W)", "g"), "\\$1");
        return this.replace(new RegExp(source, "gm"), target);
    }
}

최종적으로 이렇게 했으면 합니다

코멘트 주세요~

네. 그렇게 하는게 맞을거 같네요. smile

근데 몰라서 질문드리는 겁니다만;
m옵션이 구체적으로 하는 일이 뭔가요?
저같은 경우 g옵션만으로도 언제나 원하는 결과를 얻어서 m은 사용을 안하거든요.;;

Peris (2006-04-24 22:55:51)에 의해 마지막으로 수정

14

답글: replaceAll 버그(무한루프)

Peris 작성:

근데 몰라서 질문드리는 겁니다만;
m옵션이 구체적으로 하는 일이 뭔가요?
저같은 경우 g옵션만으로도 언제나 원하는 결과를 얻어서 m은 사용을 안하거든요.;;

var source = "a1\r\na2\r\na3\r\n";
alert(source.replace(new RegExp("^a", "gm"), "_"));

이런 경우라면 m을 써주는 쪽이 의도에 가깝게 매치가 되는 것 같아요..

'원본 문자열이 멀티라인이다' 라고 선언해준다는 것 같습니다

음.. 근데 replaceAll 함수의 경우에는 굳이 없어도 상관 없겠군요;; (일단 넣어놓고 곰곰히 생각을..)

crizin (2006-04-25 00:52:55)에 의해 마지막으로 수정

15

답글: replaceAll 버그(무한루프)

crizin 작성:
var source = "a1\r\na2\r\na3\r\n";
alert(source.replace(new RegExp("^a", "gm"), "_"));

이런 경우라면 m을 써주는 쪽이 의도에 가깝게 매치가 되는 것 같아요..

'원본 문자열이 멀티라인이다' 라고 선언해준다는 것 같습니다

음.. 근데 replaceAll 함수의 경우에는 굳이 없어도 상관 없겠군요;; (일단 넣어놓고 곰곰히 생각을..)

아.. ^, $같은 경우는 생각도 못했었네요.;;