tags:

views:

139

answers:

5

I am passing a string into my song parser method and it is failing and I can't figure out why. Every thing is returning null or 0.

My parser method is

 public static Song parseSong(String songString){
  Map<String, String> songMap = new HashMap<String, String>();
  Pattern pattern = Pattern.compile(".*<key>(.+)</key><(.+)>(.+)</.+>.*\n");
  Scanner scanner = new Scanner(songString);
  if(scanner.hasNext(pattern))
  {
     String line = scanner.next(pattern);
     Matcher matcher = pattern.matcher(line);
     MatchResult result = matcher.toMatchResult();
     songMap.put(result.group(1), result.group(3));
  }
  int count = 0, rating = 0;
  try{
     count = Integer.parseInt(songMap.get("Play Count"));
  }
  catch(Exception e)
  {
     //bury this for now will handle when rest is working
  }
  try{
     rating = Integer.parseInt(songMap.get("Rating"));
  }
  catch(Exception e)
  {
     //bury this for now will handle when rest is working
  }
  return new Song(songMap.get("Name"), songMap.get("Artist"), songMap.get("Album"),
        songMap.get("Genre"), count, rating, songMap.get("Location"));

}

      String songString = "<key>Track ID</key><integer>160</integer>\n"+
     "<key>Name</key><string>Ashley</string>\n"+
    " <key>Artist</key><string>Escape the Fate</string>\n"+
    " <key>Composer</key><string>Luca Gusella</string>\n"+
    " <key>Album</key><string>This War Is Ours</string>\n"+
  "   <key>Genre</key><string>Metal</string>\n"+
     "<key>Kind</key><string>AAC audio file</string>\n"+
  "  <key>Size</key><integer>7968219</integer>\n"+
   "  <key>Total Time</key><integer>246503</integer>\n"+
  "   <key>Track Number</key><integer>17</integer>\n"+
   "  <key>Year</key><integer>2005</integer>\n"+
   "  <key>Date Modified</key><date>2009-07-27T01:17:29Z</date>\n"+
    " <key>Date Added</key><date>2009-07-27T01:17:00Z</date>\n"+
    "<key>Play Count</key><integer>150</integer>\n"+
    " <key>Bit Rate</key><integer>256</integer>\n"+
    " <key>Sample Rate</key><integer>44100</integer>\n"+
    " <key>Comments</key><string>\"Amanda\" performed by Aisha Duo from the CD Quiet Songs, courtesy of Obliq Sound.  Written by Luca Gusella, published by Editions ObliqMusic (GEMA).  All Rights Reserved.  Used by Permission. </string>\n"+
    " <key>Skip Count</key><integer>1</integer>\n"+
    " <key>Skip Date</key><date>2009-07-27T01:46:32Z</date>\n"+
    " <key>Artwork Count</key><integer>1</integer>\n"+
    " <key>Persistent ID</key><string>A4D6F35FE9F41B58</string>\n"+
    " <key>Track Type</key><string>File</string>\n"+
    " <key>Location</key><string>file://localhost/C:/Documents%20and%20Settings/MB24244/Desktop/music/07%20Knees.m4a</string>\n"+
     "<key>File Folder Count</key><integer>4</integer>\n"+
     "afgjdhfshsgsughghanoise\n"+
     "<key>Library Folder Count</key><integer>1</integer>\n"+
     "<key>Rating</key><integer>100</integer>";

Can anyone help explain what is wrong with my approach and why the groups aren't working (which is what appears to be the problem)

+7  A: 

Why not use an XML parser to parse XML?

Although looking at the XML example it's not that nice as it's essentially modelling a map instead of modelling a <song>

Looking at your regex why are you looking for the line ending \n. It appears that you are matching each line in turn and I don't believe that these will contain the new line character.

However this method which doesn't use the scanner works. Note that I have changed the regex to remove the line ending.

    Map<String, String> songMap = new HashMap<String, String>();

 Pattern pattern = Pattern
   .compile(".*<key>(.+)</key><(.+)>(.+)</.+>.*");

 String[] lines = songString.split("\n");

 for (String line : lines) {
  Matcher matcher = pattern.matcher(line);
  if (matcher.matches()) {
   songMap.put(matcher.group(1), matcher.group(3));
  }
 }

You can probably get it to work with the Scanner too.

pjp
Well I'm writing the app this is in as a training exercise and I was hoping to learn a bit about parsing I mean getting it down to this point was easy. I'm trying to parse an itunes music library.xml for a pet project. If I can't figure it out I'm going to switch to one someone else already wrote.
faceless1_14
I have made a couple of small changes to get a working implementation of your code.
pjp
+1  A: 

Second the vote for making the XML into valid XML (single top level node), then using an XML parser.

But I would suspect the \n at the end, not sure how much Java's regex library likes that?

Douglas Leeder
A: 

Don't try to write a parser, unless you already know the the rules of that files to be parsed.

The regex you wrote doesn't follow very many of the rules of XML files.

If this is your first time writing a regex, you should probably try to learn on something a little easier to parse. Perhaps even a simple file format that you create yourself.

Brad Gilbert
+1  A: 
if(scanner.hasNext(pattern))
{
   String line = scanner.next(pattern);
   Matcher matcher = pattern.matcher(line);
   matcher.toMatchResult();
   songMap.put(result.group(1), result.group(3));
}

The matcher you created with pattern.matcher(line) is a brand-new object that it doesn't know anything about the match you just found with the Scanner. What you want is this:

MatchResult result = scanner.match();

That regex needs some work, too. If every record appears on its own line, you don't need to pad it out with ".*" on either end, and you don't need to match the linefeed. Also, I suggest you use ".+?" instead of ".+" whereever it appears. For the reason, read this.

Alan Moore
A: 

+1 to using .+?

Besides that I would suggest not using the pattern in that case as you seem to have a pretty easy file to parse. I would: - split the file line-by-line - Used simple string methods to get the content (it seems that there are only 3 different tags in your XML)

If the file format would change and become more complicated I would go with the real XMLParser and just traverse the XML tree to get what you need :)

ppow