在使用Java的等式检查(直接或间接)时,我遇到了德语“Umlaute”(ä,ö,ü,ß)的奇怪行为。 从Eclipse运行,调试或测试时,一切都按预期工作,包含“Umlaute”的输入被视为与预期相同或不同。
然而,当我使用Spring Boot构建应用程序并运行它时,对于包含“Umlaute”的单词,这些相等检查失败,即对于像“Nationalität”这样的单词。
通过Jsoup从网页检索输入,并为某些关键字提取表的内容。页面的编码是UTF-8,我已经处理好Jsoup转换它,如果不是这样的话。 源文件的编码也是UTF-8。
Connection connection = Jsoup.connect(url)
.header("accept-language", "de-de, de, en")
.userAgent("Mozilla/5.0")
.timeout(10000)
.method(Method.GET);
Response response = connection.execute();
if(logger.isDebugEnabled())
logger.debug("Encoding of response: " +response.charset());
Document doc;
if(response.charset().equalsIgnoreCase("UTF-8"))
{
logger.debug("Response has expected charset");
doc = Jsoup.parse(response.body(), baseURL);
}
else
{
logger.debug("Response doesn't have exepcted charset and is converted");
doc = Jsoup.parse(new String(response.bodyAsBytes(), "UTF-8"), baseURL);
}
logger.debug("Encoding of document: " +doc.charset());
if(!doc.charset().equals(Charset.forName("UTF-8")))
{
logger.debug("Changing encoding of document from " +doc.charset());
doc.updateMetaCharsetElement(true);
doc.charset(Charset.forName("UTF-8"));
logger.debug("Changed encoding of document to: " +doc.charset());
}
return doc;
阅读内容的示例日志输出(来自已部署的应用)。
Encoding of response: utf-8
Response has expected charset
Encoding of document: UTF-8
示例输入:
<tr><th>Nationalität:</th> <td> [...] </td> </tr>
包含ä,ö,ü或ß的单词失败的示例代码,但适用于其他单词:
Element header = row.select("th").first();
String text = header.ownText();
if("Nationalität:".equals(text))
{
// goes here in eclipse
}
else
{
// and here in deployed spring boot app
}
从Eclipse运行与内置和运行之间有什么区别吗?部署了我失踪的应用程序?这种行为可能来自何处以及如何解决这个问题?
据我所知,这不是(直接)编码问题,因为输入正确显示“Umlaute”... 由于在调试时这是不可重现的,因此我很难弄清楚到底出了什么问题。
编辑:虽然日志中的输入看起来很好(即正确显示变音符号),但我意识到它们在控制台中看起来不正确:
<th>Nationalität:</th>
我目前正在使用Mirko建议的Normalizer,如下所示:
Normalizer.normalize(input, Form.NFC);
(也尝试过NFD)。
(SpringBoot-)控制台和(logback)logoutput如何不同?
答案 0 :(得分:4)
像变音符号这样的变音符号通常可以在unicode中以两种不同的方式表示:作为单码点字符或作为两个字符的组合。这不是编码问题,它可能发生在UTF-8,UTF-16,UTF-32等。 Java的equals方法可能不会将复合字符视为等于单码点字符,即使它们看起来完全相同。 尝试查看您要比较的字符串的二进制表示,这样您就应该能够追踪差异。 您还可以使用“Character”类的方法迭代字符串并打印出所有字符的属性。也许这也有助于找出差异。
在任何情况下,如果您在“等于”的两个“边”上使用java.text.Normalizer
,它可能有所帮助,以将文本规范化为例如Unicode规范化表格C.这样,差异就像前面提到的那样应该理顺并且字符串应该按照预期进行比较。
答案 1 :(得分:1)
您是否尝试过将键码打印到控制台以查看它们在编译时是否真正匹配?也许Eclipse正在优雅地处理charset,但是当它被编译时,它归结为一些Java / System设置?
答案 2 :(得分:0)
我认为我追踪到这个独立应用程序的构建是罪魁祸首。 如上所述,从Eclipse运行时一切正常,只有当我运行独立的Spring Boot应用程序时才会出现问题。
这是使用Gradle构建的。在我的build.gradle中,我有
public class FeedsFragment extends Fragment
{
private static String URL_FEED;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.feeds, container, false);
SharedPreferences pref = this.getActivity().getSharedPreferences("ActivitySession", Context.MODE_PRIVATE);
Log.w("EduKnow:::",""+pref.getString("MOB",""));
URL_FEED = "http://api.eduknow.info/mobile/feeds/buttercup/"+pref.getString("MOB","");
new FeedTask().execute(URL_FEED);
return rootView;
}
private class FeedTask extends AsyncTask<String, String, String> {
ProgressDialog progress1;
@Override
protected void onPreExecute() {
progress1 = new ProgressDialog(getActivity(),ProgressDialog.STYLE_SPINNER);
progress1.setMessage("Updating Feeds");
progress1.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progress1.setIndeterminate(true);
}
protected String doInBackground(String... urls) {
String result1 = "";
try {
HttpGet httpGet1 = new HttpGet(urls[0]);
HttpClient client1 = new DefaultHttpClient();
HttpResponse response1 = client1.execute(httpGet1);
int statusCode = response1.getStatusLine().getStatusCode();
if (statusCode == 200) {
InputStream inputStream1 = response1.getEntity().getContent();
BufferedReader reader1 = new BufferedReader
(new InputStreamReader(inputStream1));
String line1;
while ((line1 = reader1.readLine()) != null) {
result1 += line1;
}
}
} catch (ClientProtocolException e) {
} catch (IOException e) {
}
//Log.w("PREMIERE::::",result);
Log.w("EduKnow:::",""+result1);
return result1;
}
protected void onPostExecute(String jsonString) {
// Dismiss ProgressBar
showData(jsonString);
progress1.dismiss();
}
}
private void showData(String jsonString) {
try
{
Gson gson = new Gson();
JsonParser parser = new JsonParser();
JsonArray jArray = parser.parse(jsonString).getAsJsonArray();
ArrayList<FeedPojo> feeds = new ArrayList<FeedPojo>();
for(JsonElement obj : jArray )
{
FeedPojo cse = gson.fromJson( obj , FeedPojo.class);
feeds.add(cse);
}
for(FeedPojo fx :feeds)
{
Log.w("EduKnow:::",""+fx.getFeedTitle());
}
//mAdapterPop = new CustomAdapter(posts_popular);
//mAdapterPop.notifyDataSetChanged();
//mRecyclerViewPop.setAdapter(mAdapterPop);
}
catch (Exception e)
{
Snackbar.make(getActivity().findViewById(android.R.id.content), "Check data connection", Snackbar.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
为了强制UTF-8用于编码。这应该(通常)足够了。然而,我也使用AspectJ(通过gradle-aspectj插件)显然打破了这种行为(不由自主地?)并导致使用默认编码而不是显式定义的编码。 为了解决这个问题,我添加了
compileJava.options.encoding = 'UTF-8'
到我的build.gradle,它将编码选项传递给ajc编译器。这似乎解决了常规构建的问题。
但是当从gradle运行测试时,问题仍然存在。我还不知道那里需要做什么以及为什么上述配置还不够。 现在可以单独跟踪question。