grepWin is really easy to use. Thanks for the link. When I tried this:
Find: <p id="21">Note:(?:(?!</p>).)*</p>
Replace with: <table rows="2" cols="2" width="8744"><tr rowheight="0"><td width="630" bordercolors="65793"><p id="4"><fref id="13453"/></p></td><td width="8114" bordercolors="65793"><p id="4">\1</p></td></tr></table>
it found every instance, but killed the content. The \1 was gone, and there was nothing there. But if I do this:
Find: <p id="21">Note:([A-Za-z0-9_ ><.="']*)</p>
Replace with: <table rows="2" cols="2" width="8744"><tr
rowheight="0"><td width="630" bordercolors="65793"><p
id="4"><fref id="13453"/></p></td><td
width="8114" bordercolors="65793"><p
id="4">\1</p></td></tr></table>
That works fine, but only for 36 of the 187 instances. I feel like I'm closer, but the regex is not quite right. Meanwhile, I've been reading up on doing find-replace with XSLT, and XSLT uses regex for this. So it appears that regex is my best hope after all.
Thanks all for bearing with me.