MonoTouch - 获取设备名称 - UTF8

3
我们注意到在使用MonoTouch时,当使用UIDevice.CurrentDevice.Name时,UTF8字符无法正确显示。
如果在iPad键盘上按住撇号键等一些特殊字符,则会显示为"iPad 2 ??"。(抱歉,不知道在Windows中如何显示这些字符)
有没有推荐的解决方法来获取正确的文本?我们不介意自己转换为UTF8。我也尝试从UITextField模拟这个问题,但是它运行正常--没有UTF8问题。
造成问题的原因是我们将这段文本发送到一个Web服务,并且它导致XML解析问题。
以下是XmlWriter代码的片段(_parser.WriteRequest):
            using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, new XmlWriterSettings 
                { 
#if DEBUG
                    Indent = true,
#else
                    Indent = false, NewLineHandling = NewLineHandling.None, 
#endif
                    OmitXmlDeclaration = true 
                }))
            {
                xmlWriter.WriteStartDocument();
                xmlWriter.WriteStartElement("REQUEST");
                xmlWriter.WriteAttributeString("TYPE", "EXAMPLE");
                xmlWriter.WriteEndElement();
                xmlWriter.WriteEndDocument();
            }

这里的 TextWriter 是从哪里传入的:

public Response MakeRequest(Request request)
{
    var httpRequest = CreateRequest(request);

    WriteRequest(httpRequest.GetRequestStream(), request);

    using (var httpResponse = httpRequest.GetResponse() as HttpWebResponse)
    {
        using (var responseStream = httpResponse.GetResponseStream())
        {
            var response = new Response();
            ReadResponse(response, responseStream);
            return response;
        }
    }
}

private void WriteRequest(Stream requestStream, Request request)
{
    if (request.Type == null)
    {
        throw new InvalidOperationException("Request Type was null!");
    }

    if (_logger.Enabled)
    {
        var builder = new StringBuilder();
        using (var writer = new StringWriter(builder, CultureInfo.InvariantCulture))
        {
            _parser.WriteRequest(writer, request);
        }
        _logger.Log("REQUEST: " + builder.ToString());

        using (requestStream)
        {
            using (StreamWriter writer = new StreamWriter(requestStream))
            {
                writer.Write(builder.ToString());
            }
        }
    }
    else
    {
        using (requestStream)
        {
            using (StreamWriter writer = new StreamWriter(requestStream))
            {
                _parser.WriteRequest(writer, request);
            }
        }
    }
}

_logger会将日志写入Console.WriteLine,它在#if DEBUG模式下启用。Request只是一个带有属性的存储类,很抱歉易与HttpWebRequest混淆。

我在XCode的控制台和MonoDevelop的控制台中都看到了“??”。由于收到错误消息,我还假设服务器也以奇怪的方式接收到了它们。使用UITextField.Text而不是设备描述来替代这些奇怪字符的情况下没有问题。这使我认为设备描述是罪魁祸首。

编辑:

Encoding.UTF8.GetString(Encoding.ASCII.GetBytes(UIDevice.CurrentDevice.Name));


“UTF8字符”是什么意思并不清楚 - UTF-8是一种将字符编码为二进制的方式,而不是一个字符集。你是如何创建XML的? - Jon Skeet
在MonoTouch中使用XmlWriter,服务器未知(第三方)。让我更清楚一些,UITextField.Text按预期返回字符,但从UIDevice.CurrentDevice.Name获取到的是?字符,我认为这意味着“未知字符”。Jon,我不知道你也在使用MonoTouch。 - jonathanpeppers
我没有做过任何MonoTouch开发,但我以前见过很多编码错误。你是如何使用XmlWriter的?请展示你的代码。当你说“我们得到了?字符”时,你是指Web服务接收到的,还是在调试器中看到的,还是其他什么情况?你尝试过记录字符串中每个字符的Unicode代码点吗?通常追踪这种问题只是找出你在哪里丢失数据的问题。 - Jon Skeet
我想知道这个“修复”是否是将UTF8现在明确地链接进来。例如,如果您稍后再次请求Name(而不进行编码调用),会发生什么?或者MD调试器显示了什么? - poupou
我尝试了一下,但没有成功。我不确定是否存在链接问题。 - jonathanpeppers
2个回答

4
好的,我想我知道问题所在了。您正在创建一个 `StringWriter`,它总是将其编码报告为 UTF-16(除非您覆盖 `Encoding` 属性)。然后,您从该 `StringWriter` 中获取字符串(该字符串将以 `< ?xml version="1.0" encoding="UTF-16" ? >` 开头),并将其写入默认为 UTF-8 的 `StreamWriter`中。这种编码混合引起了问题。
最简单的方法是改变您的代码,直接将流(如果您真的想要,可以是 `MemoryStream` 或者只是 `requestStream`)传递给 `XmlWriter`。这样, `XmlWriter` 就可以声明它使用的确切编码,即它实际上正在写入的二进制数据的编码 - 您没有中间步骤来搞乱事情。
或者,您可以创建 `StringWriter` 的子类,允许您指定编码。请参见此答案以获取一些示例代码。

中间步骤是为了让我们有一个StringBuilder可以传递给日志记录器,我能在所有的写入器上设置编码为UTF8吗? - jonathanpeppers
1
@Jonathan.Peppers:请查看我回答的最后一段。 - Jon Skeet
@Jonathan.Peppers:绝对不能这样做 - 这只会限制为ASCII字符。你真的应该记录XML中的字符,并仔细查看进入Web服务的内容(使用Wireshark)。 - Jon Skeet
是的,这正是我担心的。但如果文本从 Xamarin 的端口输出为 ASCII,那么我也无能为力了,对吧?我认为 @poupou 的意见会有所帮助。 - jonathanpeppers
@Jonathan.Peppers:我没有看到任何证据表明MonoTouch输出的文本是ASCII格式的。你是否已经执行了我提到的日志记录?将字符串中的每个char值记录下来,强制转换为int以确保您获得数字值。 - Jon Skeet
显示剩余3条评论

1
MonoTouch只是在从UIDevice.CurrentDevice.Name的调用中接收到的值上调用NSString.FromHandle。这就像大多数绑定内部从NSString创建string一样。
这应该会给你一个string,你可以在MonoDevelop中看到它(没有?),所以我不能排除错误的可能性。
你能告诉我们设备的确切名称吗?如果可以,请打开错误报告,我们将检查这种可能性。

按住 iPad 键盘上的撇号键。它会给你4个选项,两个弯曲的撇号是造成问题的原因。你可以在控制台中打印 UIDevice.CurrentDevice.Name 来查看我所看到的内容。 - jonathanpeppers
我将我的iPad重命名为Neptune'‘a’,它工作得很好(我在控制台和MD调试器中都能看到它...)。这可能有点冒险,但你可以尝试一下不链接(即不链接)你的应用程序吗?以防万一你的应用程序中有些代码被剥离了(但我的单元测试没有)。 - poupou
好的,我要尝试@jon的想法,也打印整数值。 - jonathanpeppers
我在新项目中无法复现这个问题,所以我猜测这是我的问题,并且Jon Skeet认为这只是一个标准的 .Net 编码问题。感谢您的帮助,我会在解决后再回复。 - jonathanpeppers
1
我想补充一下,我的一些困惑在于控制台中出现了“??”,但实际上在C#中该值是正确的。在MonoDevelop的调试器中也是正确的。似乎只有在设备上通过控制台输出时才会丢失UTF8。这也发生在XCode中,所以这可能不是Xamarin的问题--只是令人困惑。 - jonathanpeppers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接