Problem importing a JCAMP-DX mass spectral file

I have some problems importing a JCAMP-DX EI-mass spectral file using Mathematica 9. For example, if I download the mass spectrum of nonacosane in JCAMP format from NIST, http://webbook.nist.gov/cgi/cbook.cgi?ID=C630035&Mask=200#Mass-Spec using the code

data = Import["http://www.kuleuven.be/bio/ento/temp/nonacosane.dx"];
data
{{{26, 10}, {30, 30}, {41, 2269}, {51, 10}, {55, 2379}, {59, 10}, {67,
289}, {71, 7189}, {80, 20}, {84, 839}, {91, 10}, {96, 269}, {100,
169}, {110, 139}, {114, 149}, {126, 569}, {137, 20}, {141,
1089}, {152, 50}, {156, 129}, {168, 429}, {180, 30}, {184,
119}, {196, 339}, {209, 40}, {213, 10}, {225, 539}, {237,
20}, {241, 10}, {253, 469}, {265, 10}, {269, 10}, {282, 109}, {296,
90}, {310, 99}, {324, 80}, {338, 70}, {352, 70}, {366, 40}, {407,
10}}}


it seems I only get some of the peaks. Apparently, Mathematica is only importing the first pair of each row of XY values in the input file, whose structure is

##TITLE=Nonacosane
##JCAMP-DX=4.24
##DATA TYPE=MASS SPECTRUM
##ORIGIN=Japan AIST/NIMC Database- Spectrum MS-NW-5944
##OWNER=NIST Mass Spectrometry Data Center
Collection (C) 2011 copyright by the U.S. Secretary of Commerce
##CAS REGISTRY NO=630-03-5
##$NIST MASS SPEC NO=233118 ##MOLFORM=C29 H60 ##MW=408 ##$NIST SOURCE=MSDC
##XUNITS=M/Z
##YUNITS=RELATIVE INTENSITY
##XFACTOR=1
##YFACTOR=1
##FIRSTX=26
##LASTX=410
##FIRSTY=10
##MAXX=410
##MINX=26
##MAXY=9999
##MINY=10
##NPOINTS=160
##PEAK TABLE=(XY..XY)
26,10 27,289 28,90 29,1079
30,30 33,10 39,179 40,40
41,2269 42,659 43,6609 44,239
51,10 52,10 53,90 54,249
55,2379 56,1269 57,9999 58,469
59,10 63,10 65,20 66,30
67,289 68,329 69,1659 70,1129
71,7189 72,429 77,10 79,30
80,20 81,139 82,409 83,1459
84,839 85,5279 86,369 87,20
91,10 93,10 94,10 95,90
96,269 97,1189 98,659 99,2149
100,169 101,10 107,10 109,40
110,139 111,649 112,619 113,1599
114,149 123,20 124,99 125,389
126,569 127,1289 128,139 129,10
137,20 138,70 139,219 140,499
141,1089 142,149 143,10 151,10
152,50 153,139 154,449 155,929
156,129 157,10 166,50 167,99
168,429 169,839 170,119 171,10
180,30 181,70 182,379 183,779
184,119 185,10 194,20 195,60
196,339 197,679 198,109 208,20
209,40 210,279 211,589 212,109
213,10 222,20 223,30 224,259
225,539 226,99 227,10 236,10
237,20 238,219 239,489 240,99
241,10 250,10 251,20 252,229
253,469 254,99 255,10 264,10
265,10 266,189 267,419 268,99
269,10 279,10 280,159 281,399
282,109 283,20 294,149 295,369
296,90 297,10 308,119 309,359
310,99 311,10 322,99 323,319
324,80 325,10 336,90 337,289
338,70 339,20 350,60 351,229
352,70 353,10 364,40 365,129
366,40 378,20 379,90 380,20
407,10 408,469 409,159 410,30
##END=


I was just wondering what would be the best way to get around this, and have this file import correctly?

Edit: I was also wondering what is the best way to read both the actual data and any metadata stored in the JCAMP file and then have these exported to a file?

There is a space between every data pair which Mathematica apparently interprets as a multiplication. I assume these spaces should have been returns. The following code imports the file as a string, replaces the offensive space with a return and imports the result as JCAMP-DX.

ImportString[
StringReplace[
Import["http://webbook.nist.gov/cgi/cbook.cgi?JCAMP=C630035&Index=0&Type=Mass", "String"],
{x : NumberString ~~ " " ~~ y : NumberString :> x <> "\n" <> y}],
"JCAMPDX"
]


{{{26, 10}, {27, 289}, {28, 90}, {29, 1079}, {30, 30}, {33, 10}, {39, 179}, << ... >>, {378, 20}, {379, 90}, {380, 20}, {407, 10}, {408, 469}, {409, 159}, {410, 30}}}

• Many thanks for that - brilliant!! Oct 23, 2013 at 18:53
• I was just wondering if you might also know an answer to my additional question in my edited post, about importing both data & metadata, and about how to export everything to a JCAMP file? Nov 18, 2013 at 15:47
• To import metadata just replace the above "JCAMPDX" with {"JCAMPDX", "Metadata"}. Export to JCAMP does not seem to be supported. It is a simple ASCII format and I can imagine it wouldn't take you too much time to write an exporter yourself, using Write. Nov 18, 2013 at 22:20
• Ha OK many thanks - I was indeed confused about the fact that import worked OK but not the export... Nov 19, 2013 at 7:52

You can also use ReadList which is usually much faster than Import for large files:

readJCAMP[filename_String] := Module[{data, file = OpenRead[filename]},
data = ReadList[file, {Record, Record}, RecordSeparators -> {" ", ",", "\n"}];
Close[file]; ToExpression[ data[[1 ;; -2]] ]
]


Usage:

readJCAMP["http://webbook.nist.gov/cgi/cbook.cgi?JCAMP=C630035&Index=0&Type=Mass"]


{{26, 10}, {27, 289}, {28, 90}, {29, 1079}, {30, 30}, {33, 10}, {39, 179}, {40, 40}, {41, 2269}, {42, 659}, {43, 6609}, {44, 239}, {51, 10}, {52, 10}, {53, 90}, {54, 249}, {55, 2379}, {56, 1269}, {57, 9999}, {58, 469}, <<..>>, {364, 40}, {365, 129}, {366, 40}, {378, 20}, {379, 90}, {380, 20}, {407, 10}, {408, 469}, {409, 159}, {410, 30}}

EDIT

If the number of files to skip is not 25 you can define a function with that number as a second argument:

 readJCAMP[filename_String, n_Integer] := Module[{data, file = OpenRead[filename]},
data = ReadList[file, {Record, Record}, RecordSeparators -> {" ", ",", "\n"}];
Close[file]; ToExpression[ data[[1 ;; -2]] ]
]


The usage is almost the same, just include the number of lines to skip as the second argument

readJCAMP["http://webbook.nist.gov/cgi/cbook.cgi?JCAMP=C630035&Index=0&Type=Mass", 25]


EDIT 2

And if in general you don't know how many lines to skip ahead of time, then using ReadList with String manipulation should get the job done efficiently:

readJCAMP2[file_String] := Flatten@StringCases[ReadList[file, String],
NumberString ~~ "," ~~ NumberString] // StringSplit[#, ","] & // ToExpression

• This depends on your assumption that the data starts after the 25th line, which is not generally true. Oct 23, 2013 at 22:09
• @SjoerdC.deVries. You're correct. I should have included that assumption. I've updated the function to make that a second argument. Oct 23, 2013 at 23:26