正则表达式用实际字符替换所有 ASCII 字符代码

Regex replacing all ASCII character codes with actual characters

我有一个看起来像这样的字符串:

4000 BCE–5000 BCE and 600 CE–650 CE.

我正在尝试使用正则表达式搜索字符串,找到所有字符代码并将所有字符代码替换为相应的实际字符。对于我的示例字符串,我希望得到一个看起来像

的字符串

4000 BCE–5000 BCE and 600 CE–650 CE.

我试过用代码写,但我不知道该写什么:

string line = "4000 BCE–5000 BCE and 600 CE–650 CE";

listof?datatype matches = search through `line` and find all the matches to  "&#.*?;"

foreach (?datatype match in matches){
    int extractedNumber = Convert.ToInt32(Regex.(/*extract the number that is between the &# and the ?*/));

    //convert the number to ascii character
    string actualCharacter = (char) extractedNumber + "";

    //replace character code in original line
    line = Regex.Replace(line, match, actualCharacter); 
}

编辑

我的原始字符串实际上有一些 HTML 并且看起来像:

4000 <small>BCE</small>&#8211;5000 <small>BCE</small> and 600 <small>CE</small>&#8211;650 <small>CE</small>

我使用 line = Regex.Replace(note, "<.*?>", string.Empty); 删除了 <small> 标签,但显然,根据 SO 上最流行的问题之一,RegEx match open tags except XHTML self-contained tags,你真的不应该使用 RegEx 删除HTML.

如何在代表替换中做到这一点。
编辑:作为旁注,这是一个很好的正则表达式,可以删除所有标签和脚本块

<(?:script(?:\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]*?)+)?\s*>[\S\s]*?</script\s*|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:(?:(?:"[\S\s]*?")|(?:'[\S\s]*?'))|(?:[^>]*?))+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>

C#:

string line = @"4000 BCE&#8211;5000 BCE and 600 CE&#8211;650 CE";
Regex RxCode = new Regex(@"&#([0-9]+);");
string lineNew = RxCode.Replace(
    line,
    delegate( Match match ) {
        return "" + (char)Convert.ToInt32( match.Groups[1].Value);
    }
);
Console.WriteLine( lineNew );

输出:

4000 BCE-5000 BCE and 600 CE-650 CE

edit: 如果您也期望 hex 形式,您也可以处理。

 #  @"&\#(?:([0-9]+)|x([0-9a-fA-F]+));"

 &\#
 (?:
      ( [0-9]+ )                    # (1)
   |  x
      ( [0-9a-fA-F]+ )              # (2)
 )
 ;

C#:

Regex RxCode = new Regex(@"&#(?:([0-9]+)|x([0-9a-fA-F]+));");
string lineNew = RxCode.Replace(
    line,
    delegate( Match match ) {
        return match.Groups[1].Success ? 
            "" + (char)Convert.ToInt32( match.Groups[1].Value ) :
            "" + (char)Int32.Parse( match.Groups[2].Value, System.Globalization.NumberStyles.HexNumber);
    }
);

您不需要任何正则表达式即可将 XML 实体引用转换为文字字符串。

解决方案 1:XML-有效输入

这是假设您有 XML 有效输入的解决方案。

添加 using System.Xml; 命名空间并使用此方法:

public string XmlUnescape(string escaped)
{
    XmlDocument doc = new XmlDocument();
    XmlNode node = doc.CreateElement("root");
    node.InnerXml = escaped;
    return node.InnerText;
}

这样使用:

var output1 = XmlUnescape("4000 BCE&#8211;5000 BCE and 600 CE&#8211;650 CE.");

结果:

解决方案 2:具有 HTML/XML 个实体的无效 XML 输入

如果您不能将 XmlDocument 与您的字符串一起使用,因为它们包含无效的 XML 语法,您可以使用以下方法,该方法使用 HttpUtility.HtmlDecode 仅转换为已知 HTML 和 XML 个实体:

public string RevertEntities(string test)
{
   Regex rxHttpEntity = new Regex(@"(&[#\w]+;)"); // Declare a regex (better initialize it as a property/field of a static class for better performance
   string last_res = string.Empty; // a temporary variable holding a previously found entity
   while (rxHttpEntity.IsMatch(test)) // if our input has something like &#101; or &nbsp;
   {
       test = test.Replace(rxHttpEntity.Match(test).Value, HttpUtility.HtmlDecode(rxHttpEntity.Match(test).Value.ToLower())); // Replace all the entity references with there literal value (&amp; => &)
       if (last_res == test) // Check if we made any change to the string
           break; // If not, stop processing (there are some unsupported entities like &ourgreatcompany;
       else
           last_res = test; // Else, go on checking for entities
    }
    return test;
}

调用如下:

var output2 = RevertEntities("4000 BCE&#8211;5000 BCE and 600 CE&#8211;650 CE."); 

解决方案 3:HtmlAgilityPack 和 HtmlEntity.DeEntitize

使用 Manage NuGet Packages for Solution 下载并安装 HtmlAgilityPack 并使用此代码获取所有文本:

public string getCleanHtml(string html)
{
    var doc = new HtmlAgilityPack.HtmlDocument();
    doc.LoadHtml(html);
    return HtmlAgilityPack.HtmlEntity.DeEntitize(doc.DocumentNode.InnerText);
}

然后使用

var txt = "4000 <small>BCE</small>&#8211;5000 <small>BCE</small> and 600 <small>CE</small>&#8211;650 <small>CE</small>";
var clean = getCleanHtml(txt);

结果:

doc.DocumentNode.InnerText.Substring(doc.DocumentNode.InnerText.IndexOf("\n")).Trim();

您可以将 LINQ 与 HtmlAgilityPack 一起使用,下载页面(使用 var webGet = new HtmlAgilityPack.HtmlWeb(); var doc = webGet.Load(url);),等等。最好的是没有实体需要手动处理