views:

67

answers:

3

I am trying to parse some word documents in java. Some of the values are things like a date range and instead of showing up like Startdate - endDate I am getting some funky characters like so

StartDate ΓÇô EndDate

This is where word puts in a special character hypen. Can you search for these characters and replace them with a regular - or something int he string so that I can then tokenize on a "-" and what is that character - ascii? unicode or what?

Edited to add some code:

 String projDateString = "08/2010 ΓÇô Present"
                Charset charset = Charset.forName("Cp1252");
                CharsetDecoder decoder = charset.newDecoder();
                ByteBuffer buf = ByteBuffer.wrap(projDateString.getBytes("Cp1252"));
                CharBuffer cbuf = decoder.decode(buf); 
                String s = cbuf.toString();
                println ("S: " + s)

                println("projDatestring: " + projDateString)

Outputs the following:

S: 08/2010 ΓÇô Present
projDatestring: 08/2010 ΓÇô Present

Also, using the same projDateString above, if I do:

projDateString.replaceAll("\u0096", "\u2013");
projDateString.replaceAll("\u0097", "\u2014");

and then print out projDateString, it still prints as

projDatestring: 08/2010 ΓÇô Present
+3  A: 

Your problem almost certainly has to do with your encoding scheme not matching the encoding scheme Word saves in. Your code is probably using the Java default, likely UTF-8 if you haven't done anything to it. Your input, on the other hand, is likely Windows-1252, the default for Microsoft Word's .doc documents. See this site for more info. Notably,

Within Windows, ISO-8859-1 is replaced by Windows-1252, which often means that text copied from, say, a Microsoft Word document and pasted straight into a web page produces HTML validation errors.

So what does this mean for you? You'll have to tell your program that the input is using Windows-1252 encoding, and convert it to UTF-8. You can do this in varying flavors of "manually." Probably the most natural way is to take advantage of Java's built-in Charset class.

Windows-1252 is recognized by the IANA Charset Registry

Name: windows-1252
MIBenum: 2252
Source: Microsoft (http://www.iana.org/assignments/charset-reg/windows-1252) [Wendt]
Alias: None

so you it should be Charset-compatible. I haven't done this before myself, so I can't give you a code sample, but I will point out that there is a String constructor that takes a byte[] and a Charset as arguments.

Lord Torgamus
ASCII and Unicode are *Character Sets*, not encodings. When you have a particular character value from a charset, you have to decide how you're going to write that value to disk. *That's* what an encoding is.
Stephen P
@Stephen, hm, I have learned something [about semantics](http://en.wikipedia.org/wiki/Character_set#General_terminology). Neither one of us is fully right, it seems.
Lord Torgamus
Really like your edit!
Stephen P
@Stephen, thanks! I only meant to do a quick follow-up edit, but the more I researched, the more I realized that the original answer needed work, so... yeah.
Lord Torgamus
I tried a couple of different settings so far - however none have worked just yet. I forgot to mention this is word 2007. does that have a different encoding?
Derek
@Derek, Word 2007 uses all of the following encodings for English: Unicode, "Windows 1250, 1252-1254, 1257, ISO8859-x" Source: [Microsoft Office help page](http://office.microsoft.com/en-us/word-help/choose-text-encoding-when-you-open-and-save-files-HA010121249.aspx#BM4)
Lord Torgamus
+1  A: 

Probably, that character is an en dash, and the strange blurb you see is due to a difference between the way Word encodes that character and the way that character is decoded by whatever (other) system you are using to display it.

If I remember correctly from when I did some work on character encodings in Java, String instances always internally use UTF-8; so, within such an instance, you may search and replace a single character by its Unicode form. For example, let's say you would like to substitute smart quotes with plain double quotes: given a String s, you may write

s = s.replace('\u201c', '"');
s = s.replace('\u201d', '"');

where 201c and 201d are the Unicode code points for the opening and closing smart quotes. According to the link above on Wikipedia, the Unicode code point for the en dash is 2013.

Giulio Piancastelli
If Word is auto-replacing the user's character with one of its own, I'd suspect an em dash before an en dash.
Lord Torgamus
I made a simple test on a Word document before answering: on my screen the character seemed an en dash, but you may be right.
Giulio Piancastelli
In Word, if you type `2010 -- Present` it replaces the two dashes with a single *en dash*
Stephen P
As far as I can tell, the replacement is triggered even if you type a single `-`.
Giulio Piancastelli
+2  A: 

You are probably getting Windows-1252 which is a character set, not an encoding. (Torgamus - Googling for Windows-1232 didn't give me anything.)

Windows-1252, formerly "Cp1252" is almost Unicode, but keeps some characters that came from Cp1252 in their same places. The En Dash is character 150 (0x96) which falls within the Unicode C1 reserved control character range and shouldn't be there.

You can search for char 150 and replace it with \u2013 which is the proper Unicode code point for En Dash.

There are quite a few other character that MS has in the 0x80 to 0x9f range, which is reserved in the Unicode standard, including Em Dash, bullets, and their "smart" quotes.


Edit: By the way, Java uses Unicode code point values for characters internally. UTF-8 is an encoding, which Java uses as the default encoding when writing Strings to files or network connections.


Say you have

String stuff = MSWordUtil.getNextChunkOfText();

Where MSWordUtil would be something that you've written to somehow get pieces of an MS-Word .doc file. It might boil down to

File myDocFile = new File(pathAndFileFromUser);
InputStream input = new FileInputStream(myDocFile);
// and then start reading chunks of the file

By default, as you read byte buffers from the file and make Strings out of them, Java will treat it as UTF-8 encoded text. There are ways, as Lord Torgamus says, to tell what encoding should be used, but without doing that Windows-1252 is pretty close to UTF-8, except there are those pesky characters that are in the C1 control range.

After getting some String like stuff above, you won't find \u2013 or \u2014 in it, you'll find 0x96 and 0x97 instead.

At that point you should be able to do

stuff.replaceAll("\u0096", "\u2013");

I don't do that in my code where I've had to deal with this issue. I loop through an input CharSequence one char at a time, decide based on 0x80 <= charValue <= 0x9f if it has to be replaced, and look up in an array what to replace it with. The above replaceAll() is far easier if all you care about is the 1252 En Dash vs. the Unicode En Dash.

Stephen P
+1 for the `0x80 - 0x9f` info
Lord Torgamus
So my incoming string, which is coming out of the doc file, is in Cp1252, right? If I am going to strip the En Dash out of that, how do I go about that? Thought it might be something like String newString = new String(oldString.getBytes("CP1252), "UTF-8") but that doesnt seem to work - newString still prints with the funny chars, and I searched for \u2013 and \u2014 to no avail
Derek
Given a `File input` object, created from the name of the Word document on disk, you may try `char[] chars = new char[(int) (input.length())]; Reader in = new InputStreamReader(new FileInputStream(input), encoding); in.read(chars); in.close(); String s = new String(chars);` where `encoding` should be the character encoding of your Word file. From then on, `s` should internally use UTF-8, so you can search for `\u2013` or anything else with ease.
Giulio Piancastelli
@Derek: see my update. I have to do this because I get mixed input. As Giulio says in his comment and Torgamus in his answer, if you can specify that your input text is `Windows-1252` as in the 2nd parameter to the InputStreamReader constructor, you'll actually get a `\u2013` in your java String and won't have to worry about it.
Stephen P
Ok - i edited my post too with some code on my attempt - that was not working. Can you identify why?
Derek
Stephen P