我正在使用HttpClient来获取一些文件。我把内容放入一个字节数组(字节)。现在我需要检测编码。 contenttype将是html,css,JavaScript或XML contenttype。
目前我从标题中检查字符集,然后在最终检查文件的第一部分以查找字符集元标记之前检查BOM(字节顺序标记)。 通常这很好,因为没有冲突。
但是:这个顺序是否正确(如果发生冲突)?
我正确使用的代码:
Encoding encoding;
try
{
encoding = Encoding.GetEncoding(responseMessage.Content.Headers.ContentType.CharSet);
}
catch
{
using (MemoryStream ms = new MemoryStream(bytes))
{
using (StreamReader sr = new StreamReader(ms, Encoding.Default, true))
{
char[] chars = new char[1024];
sr.Read(chars, 0, 1024);
string textDefault = new string(chars);
if (sr.CurrentEncoding == Encoding.Default)
{
encoding = Global.EncodingFraContentType(textDefault);
}
else
{
encoding = sr.CurrentEncoding;
}
}
}
}
responseInfo.Text = encoding.GetString(bytes);
检测字符集/编码的正确顺序是什么?
答案 0 :(得分:2)
正确的答案不取决于订单,而是实际上给出了正确的结果,而且这里没有完美的答案。
如果存在冲突,则服务器已向您提供错误的内容。由于它不正确,因此无法做出正确的"订购,因为没有正确的错误方法。并且,标题和嵌入的元数据可能都是错误的!
即使稍微常用的编码也可能在开始时看起来像一个BOM看起来像UTF-8或UTF-16,但仍然是你提到的内容类型的有效例子,所以如果有&#39 ;然后获得BOM。
(一个例外是如果文档编辑得太糟糕,以至于部分切换编码,这是闻所未闻的,但是那些错误的内容非常错误,没有任何实际意义)。< / p>
如果内容中不包含大于0x7F的八位字节,那么它并不重要,标题和元数据都声称它是US-ASCII,UTF-8,任何ISO-8859系列的不同示例。编码,或者这些八位字节都映射到相同代码点的任何其他编码,那么你认为它不是真的重要,因为nett结果是相同的。将其视为元数据所说的内容,因为您不需要重写它以便正确匹配。
如果没有BOM的UTF-16,很可能很快就会出现这样的情况,因为所有这些格式在U + 0000到U + 00FF范围内都有很多具有特殊含义的字符(实际上,通常是U + 0020到U + 007F)因此你会有很多范围,每隔一个字符都有一个零字节。
如果它的八位字节高于0x7F并且是有效的UTF-8,那么它几乎肯定是UTF-8。 (同样地,如果它不是UTF-8并且八位字节高于0x7F那么它几乎肯定不会被误认为是UTF-8。)
最棘手的合理常见情况是,如果你有两种不同的编码声称存在冲突,这两种编码都是单字节每字符编码,并且存在0x80-0xFF范围内的八位字节。这是您无法确定的情况。如果一个编码是另一个编码的子集(特别是当排除C1控件时),那么你可以选择超集,但这需要存储有关这些编码的知识,以及大量的工作。大部分时间我都倾向于抛出异常,当它在日志中找到时,看看我是否可以获得修复bug的来源,或者特殊情况下的来源,但是没有如果你正在处理与你可能没有关系的大量不同来源,那就行了起来。唉,这里没有完美的答案。
值得注意的是,有时标头和嵌入式元数据都会错误地彼此一致。常见的情况是CP-1252中的内容,但声称属于ISO-8859-1。
答案 1 :(得分:1)
根据W3C Faq
如果文件开头有UTF-8字节顺序标记(BOM),则Internet Explorer 10或11以外的最新浏览器版本将使用它来确定页面的编码是UTF-8。它具有比任何其他声明更高的优先级,包括HTTP标头。
当谈到http-header vs meta BOM优先时,只要meta在第一个1024之内它就可以优先,尽管没有严格的规则。
答案 2 :(得分:0)
结论-重要性顺序:
unicode
编码显示)。xml prolog
,html meta tag
中指定编码
或@charset in css
。要阅读,您需要先解码
部分文档,例如使用“ Windows-1252”编码。standard of the web
,今天是最常用的。ISO-8859-1
”,请改用“ Windows-1252
”(在html5中是必需的-请在Wikipedia中阅读更多内容现在尝试使用找到的编码对文档进行解码。如果error handling
打开,则可能会失败!在这种情况下:
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"
;
(它适用于单qout和双qoutes)。
您将需要:
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
'。