结论 - 按重要性排序:
-
字节顺序标记 (BOM):如果存在,这是 AUTHORATIVE,因为它是
由实际保存文件的编辑器添加(这只能是
存在于
unicode 编码上)。
-
Content-Type charset(在服务器设置的标头中):对于动态创建/处理的文件,它应该存在(因为
服务器知道),但可能不适用于静态文件(服务器只是
发送那些)。
-
内联字符集:对于 xml、html 和 css,可以在文档中指定编码,
xml prolog、html meta tag
或@charset in css。要阅读,您需要先解码
文档的一部分使用例如 'Windows-1252' 编码。
-
假设 utf-8。这是
standard of the web,是目前使用最多的。
- 如果找到的编码等于“
ISO-8859-1”,请改用“Windows-1252”(在 html5 中是必需的 - 更多信息请参阅 Wikipedia
现在尝试使用找到的编码解码文档。如果error handling 被打开,那可能会失败!在这种情况下:
-
使用“Windows-1252”。这是旧 Windows 文件中的标准,并且在最后一次尝试时工作正常(那里仍然有很多旧文件)。
这将出现
never throw 错误。但是,这当然可能是错误的。
我已经制作了一个实现这一点的方法。我使用的regex 能够找到指定为的编码:
Xml:<?xml version="1.0" encoding="utf-8"?> 或 <?xml encoding="utf-8"?>
html:<meta charset="utf-8" /> 或 <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
css:@charset "utf-8";
(它适用于单引号和双引号)。
你需要:
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
这里是返回解码字符串的方法(参数是HttpClient和Uri):
public static async Task<string> GetString(HttpClient httpClient, Uri url)
{
byte[] bytes;
Encoding encoding = null;
Regex charsetRegex = new Regex(@"(?<=(<meta.*?charset=|^\<\?xml.*?encoding=|^@charset[ ]?)[""']?)[\w-]+?(?=[""';\r\n])",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture);
using (HttpResponseMessage responseMessage = await httpClient.GetAsync(url).ConfigureAwait(false))
{
responseMessage.EnsureSuccessStatusCode();
bytes = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
string headerCharset = responseMessage?.Content?.Headers?.ContentType?.CharSet;
byte[] buffer = new byte[0x1000];
Array.Copy(bytes, buffer, Math.Min(bytes.Length, buffer.Length));
using (MemoryStream ms = new MemoryStream(buffer))
{
using (StreamReader sr = new StreamReader(ms, Encoding.GetEncoding("Windows-1252"), true, buffer.Length, true))
{
string testString = await sr.ReadToEndAsync().ConfigureAwait(false);
if (!sr.CurrentEncoding.Equals(Encoding.GetEncoding("Windows-1252")))
{
encoding = sr.CurrentEncoding;
}
else if (headerCharset != null)
{
encoding = Encoding.GetEncoding(headerCharset, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);
}
else
{
string inlineCharset = charsetRegex.Match(testString).Value;
if (!string.IsNullOrEmpty(inlineCharset))
{
encoding = Encoding.GetEncoding(inlineCharset, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);
}
else
{
encoding = new UTF8Encoding(false, true);
}
}
if (encoding.Equals(Encoding.GetEncoding("iso-8859-1")))
{
encoding = Encoding.GetEncoding("Windows-1252", EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);
}
}
}
using (MemoryStream ms = new MemoryStream(bytes))
{
try
{
using (StreamReader sr = new StreamReader(ms, encoding, false, 0x8000, true))
{
return await sr.ReadToEndAsync().ConfigureAwait(false);
}
}
catch (DecoderFallbackException)
{
ms.Position = 0;
using (StreamReader sr = new StreamReader(ms, Encoding.GetEncoding("Windows-1252"), false, 0x8000, true))
{
return await sr.ReadToEndAsync().ConfigureAwait(false);
}
}
}
}
}
您应该将方法调用包装在 try/catch 中,因为如果请求失败,HttpClient 会抛出错误。
更新:
在.Net Core 中,您没有“Windows-1252”编码(恕我直言,大错特错),所以在这里您必须使用“ISO-8859-1”。