views:

478

answers:

8

Basically, I want to do "zipzam&&&?&&&?&&&" -> "zipzam%26%26%26?&&&?&&&". I can do that without regex many different ways, but it'd cleanup things a tad bit if I could do it with regex.

Thanks

Edit: "zip=zam&&&=?&&&?&&&" -> "zip=zam%26%26%26=?&&&?&&&" should make things a little clearer.

Edit: "zip=zam=&=&=&=?&&&?&&&" -> "zip=zam=%26=%26=%26=?&&&?&&&" should make things clearer.

However, theses are just examples. I still want to replace all '&' before the first '?' no matter where the '&' are before the first '?' and no matter if the '&' are consecutive or not.

+1  A: 

This should do it:

^[^?]*&[^?]*\?
Michael Borgwardt
I'm not sure how you would use this pattern to achieve the goal,
AnthonyWJones
+1  A: 

In this case regexes are really not the most appropiate things to use. A simple search for the first index of '?' and then replacing each '&' character would be best. However, if you really want a regex then this should do the job.

(?:.*?(&))*?\?
Noldorin
+1  A: 

Or this one, I think:

^[^?]*(&+?)\?
José Leal
José Leal
See, the first group?
José Leal
I think my tests are in error. I've removed my comments.
Will
+3  A: 

you need negative lookbehinds which are tricky to replicate in JS, but fortunately there are ways and means:

var x = "zipzam&&&?&&&?&&&";

x.replace(/(&+)(?=.*?\?)/,function ($1) {for(var i=$1.length, s='';i;i--){s+='%26';} return s;})

commentary: this works because it's not global. The first match is therefore a given, and the trick of replacing all of the matching "&" chars 1:1 with "%26" is achieved with the function loop


edit: a solution for unknown groupings of "&" can be achieved simply (if perhaps a little clunkily) with a little modification. The basic pattern for replacer methods is infinitely flexible.

var x = "zipzam&foo&bar&baz?&&&?&&&";

var f = function ($1,$2)
{
  return $2 + ($2=='' || $2.indexOf('?')>-1 ? '&' : '%26')
}

x.replace(/(.*?)&(?=.*?\?)/g,f)
annakata
This seems to produce the result I want:alert("zip=zamHowever, am I missing something?
Shadow2531
annakata
Not sure why I'm getting different results. I'll bow to you guys.
Will
@Shadow - nope, that could be a clean solution for you. The encodeURIComponent method will achieve the same result as the anon method above in this case, since the first argument to the method is still going to be the match, you gain built-in handling, but lose some flexibility. It's a good solution
annakata
@Will - I'm just running this on an html page in my editor, can't imagine why you'd see anything different?
annakata
Shadow2531
Ah, I wondered if it might. Perhaps your question could clarify that, but nonetheless it's easy enough to extend too (see edit)
annakata
Yes, clarified the question and thanks. My fault for not being clear. I didn't expect my examples to distract from the title. Will be careful next time.
Shadow2531
+1  A: 

This close enough to what you are after:-

alert("zipzam&&&?&&&?&&&".replace(/^([^&\?]*)(&*)\?/, function(s, p, m)
{
for (var i = 0; i < m.length; i++) p += '%26';
return  p +'?';
}));
AnthonyWJones
I think I'm getting some incorrect results from my testing. I'm removing all my comments.
Will
+4  A: 

This should do it:

"zip=zam=&=&=&=?&&&?&&&".replace(/^[^?]+/, function(match) { return match.replace(/&/g, "%26"); });
Gumbo
@Gumbo I think your last one does the trick. Let me double check for a bit.
Shadow2531
Shadow2531
nice simple semi-recursion solution, better presented than my efforts +1 :)
annakata
+1  A: 

Since the OP only wants to match ampersands before the first question mark, slightly modifying Michael Borgwardt's answer gives me this Regex which appears to be appropriate :

^[^?&]*(\&+)\?

Replace all matches with "%26"

This will not match zipzam&&abc?&&&?&&& because the first "?" does not have an ampersand immediately before it.

Cerebrus
A: 

As far as the javascript part, I can't answer that. But I can give you a regex that will match, separately, each '&' after the first bit of text and before the first '?'

(?<!\?.{0,2})&+?

Which, in human terms, says:

Match as few &'s as possible that are NOT after '?', '?X' or '?XX' where X is any character.

It only matches '&'s, so it won't match anything in the initial bit of text. It's a non-greedy match (+?) so it matches the first & and stops, then matches the second & and stops, etc.

Will
The javascript part is trivial compared to the regex part. If you don't like, fine. You shouldn't downvote people who provide 80% of your solution.
Will
fwiw, the problem here is that JS doesn't support the neg lookbehind (or any lookbehind), oh and + and ? at the end are working against each other
annakata
@will: Down vote removed, sorry. @annakata: is that true of all implementations of Javascript or MS JScript, I can't remember if Ecma defines the Regex it supports.
AnthonyWJones
@Anthony - ECMA does define it's regex grammar in section 15.10. It describes lookaheads *in depth*, but not a word on lookbehind. However the ?< and ?<! formats are generally described as ECMA style, which leaves me, for one, thoroughly confused.
annakata