【问题标题】:Android Java UTF-8 HttpClient ProblemAndroid Java UTF-8 HttpClient 问题
【发布时间】:2010-12-18 21:50:48
【问题描述】:

我在使用从网页抓取的 JSON 数组时遇到了奇怪的字符编码问题。服务器正在发回此标头:

内容类型文本/javascript;字符集=UTF-8

我还可以查看 Firefox 或任何浏览器中的 JSON 输出,并且 Unicode 字符正确显示。响应有时会包含来自另一种语言的带有重音符号等的单词。但是,当我将其拉下并将其放入 Java 中的字符串时,我得到了那些奇怪的问号。这是我的代码:

HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, "utf-8");
params.setBooleanParameter("http.protocol.expect-continue", false);

HttpClient httpclient = new DefaultHttpClient(params);

HttpGet httpget = new HttpGet("http://www.example.com/json_array.php");
HttpResponse response;
    try {
        response = httpclient.execute(httpget);

        if(response.getStatusLine().getStatusCode() == 200){
            // Connection was established. Get the content. 

            HttpEntity entity = response.getEntity();
            // If the response does not enclose an entity, there is no need
            // to worry about connection release

            if (entity != null) {
                // A Simple JSON Response Read
                InputStream instream = entity.getContent();
                String jsonText = convertStreamToString(instream);

                Toast.makeText(getApplicationContext(), "Response: "+jsonText, Toast.LENGTH_LONG).show();

            }

        }


    } catch (MalformedURLException e) {
        Toast.makeText(getApplicationContext(), "ERROR: Malformed URL - "+e.getMessage(), Toast.LENGTH_LONG).show();
        e.printStackTrace();
    } catch (IOException e) {
        Toast.makeText(getApplicationContext(), "ERROR: IO Exception - "+e.getMessage(), Toast.LENGTH_LONG).show();
        e.printStackTrace();
    } catch (JSONException e) {
        Toast.makeText(getApplicationContext(), "ERROR: JSON - "+e.getMessage(), Toast.LENGTH_LONG).show();
        e.printStackTrace();
    }

private static String convertStreamToString(InputStream is) {
    /*
     * To convert the InputStream to String we use the BufferedReader.readLine()
     * method. We iterate until the BufferedReader return null which means
     * there's no more data to read. Each line will appended to a StringBuilder
     * and returned as String.
     */
    BufferedReader reader;
    try {
        reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
    } catch (UnsupportedEncodingException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    StringBuilder sb = new StringBuilder();

    String line;
    try {
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return sb.toString();
}

如您所见,我在 InputStreamReader 上指定了 UTF-8,但每次我通过 Toast 查看返回的 JSON 文本时,都会出现奇怪的问号。我在想我需要将 InputStream 发送到 byte[] 吗?

提前感谢您的帮助。

【问题讨论】:

    标签: java android httpclient


    【解决方案1】:

    试试这个:

    if (entity != null) {
        // A Simple JSON Response Read
        // InputStream instream = entity.getContent();
        // String jsonText = convertStreamToString(instream);
    
        String jsonText = EntityUtils.toString(entity, HTTP.UTF_8);
    
        // ... toast code here
    }
    

    【讨论】:

    • 感谢您的回复。我添加了您的更改并为 EntityUtils 导入了额外的 Apache 内容,但现在该应用程序只是在 EntityUtils.toString 行上意外终止。程序编译并运行,但在调用 toString 之前我需要对实体做一些事情吗?
    • 没关系。我是个白痴,把我的网址搞砸了。有用!字符渲染正确!
    • @Michael:这个答案很好,如果我问这个问题我会接受这个。
    • @SK9 感谢您的提醒。我完全忘记点击复选标记。抱歉,Arhimed。
    • 发帖UrlEncodedFormEntity encodedFormEntity = new UrlEncodedFormEntity(nameValuePairs, HTTP.UTF_8); post.setEntity(encodedFormEntity); :)
    【解决方案2】:

    @Arhimed 的答案就是解决方案。但我看不出您的convertStreamToString 代码有任何明显错误。

    我的猜测是:

    1. 服务器将 UTF 字节顺序标记 (BOM) 放在流的开头。标准 Java UTF-8 字符解码器不会删除 BOM,因此它很可能最终会出现在结果字符串中。 (但是,EntityUtils 的代码似乎也没有对 BOM 做任何事情。)
    2. 您的convertStreamToString 一次读取一行字符流,并使用硬连线'\n' 作为行尾标记重新组合它。如果您要将其写入外部文件或应用程序,您可能应该使用特定于平台的行尾标记。

    【讨论】:

      【解决方案3】:

      只是您的 convertStreamToString 不遵守 HttpRespnose 中设置的编码。如果您查看EntityUtils.toString(entity, HTTP.UTF_8) 内部,您将看到EntityUtils 会首先查找HttpResponse 中是否设置了编码,如果有,EntityUtils 会使用该编码。如果实体中没有设置编码,它只会回退到参数中传递的编码(在本例中为 HTTP.UTF_8)。

      所以你可以说你的 HTTP.UTF_8 是在参数中传递的,但它永远不会被使用,因为它是错误的编码。因此,这里是使用 EntityUtils 的辅助方法更新您的代码。

                 HttpEntity entity = response.getEntity();
                 String charset = getContentCharSet(entity);
                 InputStream instream = entity.getContent();
                 String jsonText = convertStreamToString(instream,charset);
      
          private static String getContentCharSet(final HttpEntity entity) throws ParseException {
          if (entity == null) {
              throw new IllegalArgumentException("HTTP entity may not be null");
          }
          String charset = null;
          if (entity.getContentType() != null) {
              HeaderElement values[] = entity.getContentType().getElements();
              if (values.length > 0) {
                  NameValuePair param = values[0].getParameterByName("charset");
                  if (param != null) {
                      charset = param.getValue();
                  }
              }
          }
          return TextUtils.isEmpty(charset) ? HTTP.UTF_8 : charset;
      }
      
      
      
      private static String convertStreamToString(InputStream is, String encoding) {
          /*
           * To convert the InputStream to String we use the
           * BufferedReader.readLine() method. We iterate until the BufferedReader
           * return null which means there's no more data to read. Each line will
           * appended to a StringBuilder and returned as String.
           */
          BufferedReader reader;
          try {
              reader = new BufferedReader(new InputStreamReader(is, encoding));
          } catch (UnsupportedEncodingException e1) {
              // TODO Auto-generated catch block
              e1.printStackTrace();
          }
          StringBuilder sb = new StringBuilder();
      
          String line;
          try {
              while ((line = reader.readLine()) != null) {
                  sb.append(line + "\n");
              }
          } catch (IOException e) {
              e.printStackTrace();
          } finally {
              try {
                  is.close();
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          return sb.toString();
      }
      

      【讨论】:

        【解决方案4】:

        Archimed 的回答是正确的。但是,这可以通过在 HTTP 请求中提供额外的标头来完成:

        Accept-charset: utf-8
        

        无需删除任何内容或使用任何其他库。

        例如,

        GET / HTTP/1.1
        Host: www.website.com
        Connection: close
        Accept: text/html
        Upgrade-Insecure-Requests: 1
        User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.10 Safari/537.36
        DNT: 1
        Accept-Encoding: gzip, deflate, sdch
        Accept-Language: en-US,en;q=0.8
        Accept-Charset: utf-8
        

        很可能您的请求没有任何 Accept-Charset 标头。

        【讨论】:

          【解决方案5】:

          从响应内容类型字段中提取字符集。您可以使用以下方法来做到这一点:

          private static String extractCharsetFromContentType(String contentType) {
              if (TextUtils.isEmpty(contentType)) return null;
          
              Pattern p = Pattern.compile(".*charset=([^\\s^;^,]+)");
              Matcher m = p.matcher(contentType);
          
              if (m.find()) {
                  try {
                      return m.group(1);
                  } catch (Exception e) {
                      return null;
                  }
              }
          
              return null;
          }
          

          然后使用提取的字符集创建InputStreamReader

          String charsetName = extractCharsetFromContentType(connection.getContentType());
          
          InputStreamReader inReader = (TextUtils.isEmpty(charsetName) ? new InputStreamReader(inputStream) :
                              new InputStreamReader(inputStream, charsetName));
                      BufferedReader reader = new BufferedReader(inReader);
          

          【讨论】:

            猜你喜欢
            • 2015-05-24
            • 2017-07-30
            • 2011-01-11
            • 2011-05-25
            • 2013-11-12
            • 2012-02-22
            • 2014-10-13
            • 2011-04-19
            • 2012-06-11
            相关资源
            最近更新 更多