【问题标题】:How to parse a string that is in a different encoding from java如何解析与java编码不同的字符串
【发布时间】:2010-10-25 16:23:25
【问题描述】:

我有一个从 Word 文档中读入的字符串。我认为它是“Cp1252”编码。 Java 使用 UTF8。

如何在该字符串中搜索 Cp1252 中的那些特殊字符并将其替换为适当的 UTF8 字符?

具体来说,我想用普通的“-”替换“En Dash”字符

以下代码块采用来自 Word 文档的 projDateString,并尝试做这样的事情

    char[] test = projDateString.getBytes("Cp1252");
    for(int i = 0; i < test.length; i++){
    System.out.println "test["+ i + "] = " + Integer.toHexString((byte)test[i]);
    }
    String projDateString2 = new String(test);
    projDateString2.replaceAll("\0x96", "\u2013");
    System.out.println("projDateString2: " + projDateString)

我不确定我是否正确设置了 projDateString2。如您所见,当我使用 Cp1252 编码在字符串上获取字节时,该破折号的十六进制值为 ffffff96。如果我使用 UTF8 获取字节,它会以 3 个十六进制值的形式出现,而不是 1 个。

这给了我以下输出:

test[0] = 30
test[1] = 38
test[2] = 2f
test[3] = 32
test[4] = 30
test[5] = 31
test[6] = 30
test[7] = 20
test[8] = ffffff96
test[9] = 20
test[10] = 50
test[11] = 72
test[12] = 65
test[13] = 73
test[14] = 65
test[15] = 6e
test[16] = 74
projDateString2: 08/2010 ΓÇô Present

正如你所看到的,替换什么也没做,而 println 仍然给我垃圾字符而不是纯文本“-”

【问题讨论】:

标签: java character-encoding


【解决方案1】:

Java 字符串在 UTF-16 中总是,至少就 API 而言……但您通常可以将它们视为“Unicode”。它们是 UTF-16 的事实仅在涉及基本多语言平面之外的字符时才真正相关,即 Unicode 值高于 U+FFFF。它们必须在 Java 中表示为 代理对。但我认为你不需要担心你的情况。因此,只需将字符串中的值视为没有特定编码的“Unicode 文本”……尤其是在 UTF-8 或 CP1252 中绝对 not。这些是用于将 二进制 数据(例如字节数组)转换为文本数据(例如字符串)的编码。

你不应该在没有指定编码的情况下使用String.getBytes()new String(byte[]) - 这就是的问题。那些总是使用平台默认编码 - 这几乎是总是错误的选择。

你说你“有一个我从 Word 文档中读入的字符串”——你是如何读入的?它是如何开始生命的?

如果你有 bytes 并且你知道相关的编码,你应该使用:

String text = new String(bytes, encoding);

您永远不必处理使用错误编码创建的字符串 - 如果您到了那个阶段,您几乎 必然 冒着信息丢失的风险。尽可能早地解决问题,而不是稍后尝试修复数据。

接下来要理解的是,Java 中的String 类是不可变的。对字符串调用replaceAll不会更改现有字符串。相反,它将返回一个 new 字符串,其中包含所做的替换。

所以这个说法:

projDateString2.replaceAll("\0x96", "\u2013");

永远做你想做的事。即使其他一切都正确,您也应该使用:

projDateString2 = projDateString2.replaceAll("\0x96", "\u2013");

(或类似的东西)。我不认为实际上 做你想做的事,但是当其他一切都整理好时,你需要意识到这一点。

【讨论】:

  • 我会重新考虑这个评论,因为你在我写的时候编辑了它。
  • 没有为上述方法指定编码仅仅意味着它们使用默认的平台编码。如果未指定 -Dfile.encoding,则为 UTF-8。
  • @Bozho:在 一些 平台上是 UTF-8,但不是在所有平台上。依赖它基本上是一个糟糕的举动。我会编辑这个。
  • 你不应该依赖默认值是完全正确的
  • 我正在使用docx4j打开word文档。它似乎正在使用 FileInputStream 并且可以在这里看到加载方法:dev.plutext.org/trac/docx4j/browser/trunk/docx4j/src/main/java/…
【解决方案2】:

转换通常是这样完成的:

String properlyEncoded = 
    new String(original.getBytes(originalEncoding), newEncoding);

请注意,在转换过程中丢失某些信息的可能性不大。

【讨论】:

  • 好的,所以我做了 String projDateString2 = new String(projDateString.getBytes("Cp1252"), "UTF-16");我仍然无法让 replaceAll 正常工作
  • 这种转换很容易丢失,因为原来的错误转换很容易丢失信息。
  • @Jon Skeet 是的。但我认为,你无法避免损失。
【解决方案3】:

首先,您需要确保正确地将 CP1252 字节转换为 Java 的 character representation(即 UTF-16)。由于您正在使用一个库来解析 .docx 文件,这可能已经发生了。

现在您需要做的就是调用projDateString.replace('\u2013', '-') 并对返回值做一些事情。不需要replaceAll(),因为您没有使用正则表达式。

【讨论】:

  • 我正在使用docx4j打开word文档。它似乎正在使用 FileInputStream 并且可以在这里看到加载方法:dev.plutext.org/trac/docx4j/browser/trunk/docx4j/src/main/java/…
  • 感谢关于返回值的提示 - 我在代码中输入正确..只是没有进入我的 SO 问题
  • 更新了我的答案,您正试图从“En Dash”变为“-”,对吗?否则你将不得不交换 replace() 参数。
猜你喜欢
  • 1970-01-01
  • 2016-06-17
  • 2018-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-01
相关资源
最近更新 更多