# Crazy gsub/regex scheme - can this be done better?

**URL:** <https://www.ruby-forum.com/t/crazy-gsub-regex-scheme-can-this-be-done-better/102158>\
**Category:** Ruby\
**Created:** [August 11, 2006, 9:47pm UTC](https://www.ruby-forum.com/t/crazy-gsub-regex-scheme-can-this-be-done-better/102158 "2006-08-11T21:47:21Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![weyus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/w/7ab992/32.png) [@weyus](https://www.ruby-forum.com/u/weyus)\
**Post date:** [August 11, 2006, 9:47pm UTC](https://www.ruby-forum.com/t/crazy-gsub-regex-scheme-can-this-be-done-better/102158/1 "2006-08-11T21:47:21Z")

</div>

All,

I have a method (that I believe to be working) that will take arbitrary  
HTML and quote all of the non-quoted attributes (so href=junk would  
become href=“junk”).

The method is below. As you can see it’s a gsub within a gsub, where  
the first gsub regex basically identifies any tag that has at least one  
unquoted attribute, and then the inner gsub fixes ALL of the quoted  
attributes.

QUESTION: Is there a way to do this with one gsub, or is this scheme  
really the only valid way to handle it?

Thanks,  
Wes

#Make sure that every tag attribute is contained within either single or  
double quotes.  
#The initial regex is to find at least one “bad” attribute value pair  
#The “inner” regex is to actually fix ALL of the “bad” attribute value  
pairs  
private  
def ensure\_quoted\_attributes  
@html.gsub!(/\<(?!!)[a-zA-Z0-9]+\s+ #Non-comment tag  
name, followed by whitespace  
(?:[a-zA-Z0-9]+?=([’"])(._?)\1\s_)_? #Any number of valid  
attribute-value pairs (attribute=“value”), not-greedy  
[a-zA-Z0-9]+?=[^"’\s\>]+\s_? #An unquoted  
attribute-value pair (attribute=value)  
._?\> #Rest of tag  
/mix) { |s|  
s.gsub(/(\s+[a-zA-Z0-9]+?=)([^"’\s\>]+)(\s_?)/) {  
|sub\_s| “#{$1}”#{$2}"#{$3}" }  
}  
end

---

<div class="post-metadata">

**Author:** ![weyus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/w/7ab992/32.png) [@weyus](https://www.ruby-forum.com/u/weyus)\
**Post date:** [August 11, 2006, 11:17pm UTC](https://www.ruby-forum.com/t/crazy-gsub-regex-scheme-can-this-be-done-better/102158/2 "2006-08-11T23:17:32Z")

</div>

Wes G. wrote:

> All,
> 
> I have a method (that I believe to be working) that will take arbitrary  
> HTML and quote all of the non-quoted attributes (so href=junk would  
> become href=“junk”).

You might want to just look into using Tidy, hpricot  
or something that should fix broken HTML to compliant  
XHTML. They would probably do this for you.

> The method is below. As you can see it’s a gsub within a gsub, where  
> the first gsub regex basically identifies any tag that has at least one  
> unquoted attribute, and then the inner gsub fixes ALL of the quoted  
> attributes.
> 
> QUESTION: Is there a way to do this with one gsub, or is this scheme  
> really the only valid way to handle it?
> 
> Thanks,  
> Wes
> 
> #Make sure that every tag attribute is contained within either single or  
> double quotes.  
> #The initial regex is to find at least one “bad” attribute value pair  
> #The “inner” regex is to actually fix ALL of the “bad” attribute value  
> pairs  
> private  
> def ensure\_quoted\_attributes  
> @html.gsub!(/\<(?!!)[a-zA-Z0-9]+\s+ #Non-comment tag  
> name, followed by whitespace  
> (?:[a-zA-Z0-9]+?=([’"])(._?)\1\s_)_? #Any number of valid  
> attribute-value pairs (attribute=“value”), not-greedy  
> [a-zA-Z0-9]+?=[^"’\s\>]+\s_? #An unquoted  
> attribute-value pair (attribute=value)  
> ._?\> #Rest of tag  
> /mix) { |s|  
> s.gsub(/(\s+[a-zA-Z0-9]+?=)([^"’\s\>]+)(\s_?)/) {  
> |sub\_s| “#{$1}”#{$2}"#{$3}" }  
> }  
> end

---

<div class="post-metadata">

**Author:** ![weyus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/w/7ab992/32.png) [@weyus](https://www.ruby-forum.com/u/weyus)\
**Post date:** [August 11, 2006, 11:37pm UTC](https://www.ruby-forum.com/t/crazy-gsub-regex-scheme-can-this-be-done-better/102158/3 "2006-08-11T23:37:28Z")

</div>

The problem with those kind of parsers (I’m using Rubyful Soup to some  
degree) is that they try to “fix” the HTML for you and sometimes cause  
it to be rendered incorrectly compared to the original “incorrect”  
implementation.

WG

---

<div class="post-metadata">

**Author:** ![weyus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/w/7ab992/32.png) [@weyus](https://www.ruby-forum.com/u/weyus)\
**Post date:** [August 12, 2006, 1:49am UTC](https://www.ruby-forum.com/t/crazy-gsub-regex-scheme-can-this-be-done-better/102158/4 "2006-08-12T01:49:05Z")

</div>

[ INSANE COMMENT: I just want to say that the black magic that is  
regexes is so powerful and alluring that I can’t resist it and at the  
same time so repulsive that I never want to do it again. 🙂 🙂 🙂 ]

Update - my original scheme would fail when there was an attribute like

content=“text/html; charset=UTF-8”

because the latter half would be seen as needing to be charset=“UTF-8”.

Thus, I became intimate with negative zero-width lookahead.

Here’s what I believe to be a more correct solution (I apologize for the  
formatting but I wanted to leave the comments in here).

Wes

#Make sure that every tag attribute is contained within either single or  
double quotes.  
#The initial regex is to find at least one “bad” attribute value pair  
#The “inner” regex is to actually fix ALL of the “bad” attribute value  
pairs  
private  
def ensure\_quoted\_attributes  
@html.gsub!(/\<(?!!)[a-zA-Z0-9]+\s+ #Non-comment tag name,  
followed by whitespace  
(?:[a-zA-Z-]+?=([’"])._?\1\s_)_? #Any number of valid  
attribute-value pairs (attribute=“value”), not-greedy  
[a-zA-Z-]+?=[^"’\s\>]+\s_? #An unquoted  
attribute-value pair (attribute=value)  
._?\> #Rest of tag  
/mix) { |s| #For each tag gotten  
from the first regex, globally substitute into it based on…  
s.gsub(/(\s+[a-zA-Z-]+=) #Attribute name  
(?!([’"])[^’"]_?\2[\s\>]) #If the value  
looks like “stuff”, then don’t match, it’s fine  
(?![^’"]\*?[’"][\s\>]) #If the value  
looks like stuff", then don’t match, it must be the tail end of another  
attribute-value pair  
([^’"\s\>]+) #Get the  
no-whitespace, no-’\>’, no quote text  
/mix) { |sub\_s| “#{$1}”#{$3}"" }  
#Substitute attribute name=“attribute value”  
}  
end
