ms-msrun 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -4,7 +4,7 @@ A library for working with LC/MS runs.
4
4
 
5
5
  == Examples
6
6
 
7
- The following example will work on *mzXML*, *mzData* ( and *mzML* files, shortly)!
7
+ The following example will work on *mzXML*, *mzData* ( and *mzML* files in near future)!
8
8
 
9
9
  require "ms/msrun"
10
10
 
@@ -55,8 +55,7 @@ The following example will work on *mzXML*, *mzData* ( and *mzML* files, shortly
55
55
  gem install ms-msrun
56
56
 
57
57
  The library currently relies on xmlparser (though LibXML is close to being
58
- supported). After installation of ms-msrun (which will automatically install
58
+ supported). After installation of ms-msrun (which should automatically install
59
59
  `axml`) issue this command to get instructions on installing xmlparser:
60
60
 
61
- ruby -rubygems -e 'require "axml" \
62
- puts AXML::Autoload.install_instructions(:xmlparser)'
61
+ ruby -rubygems -e 'require "axml"; puts AXML::Autoload.install_instructions(:xmlparser)'
data/Rakefile CHANGED
@@ -35,11 +35,26 @@ Rake::RDocTask.new do |rd|
35
35
  rd.options.push( *rdoc_options )
36
36
  end
37
37
 
38
- desc "create and upload docs to server"
39
- task :upload_docs => [:rdoc] do
40
- sh "scp -r #{rdoc_dir}/* jtprince@rubyforge.org:/var/www/gforge-projects/mspire/ms-msrun/"
38
+
39
+ desc "Publish RDoc to RubyForge"
40
+ task :publish_rdoc => [:rdoc] do
41
+ require 'yaml'
42
+
43
+ config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
44
+ host = "#{config["username"]}@rubyforge.org"
45
+
46
+ rsync_args = "-v -c -r"
47
+ remote_dir = "/var/www/gforge-projects/mspire/projects/#{NAME}"
48
+ local_dir = "rdoc"
49
+
50
+ sh %{rsync #{rsync_args} #{local_dir}/ #{host}:#{remote_dir}}
41
51
  end
42
52
 
53
+ #desc "create and upload docs to server"
54
+ #task :upload_docs => [:rdoc] do
55
+ # sh "scp -r #{rdoc_dir}/* jtprince@rubyforge.org:/var/www/gforge-projects/mspire/projects/ms-msrun/"
56
+ #end
57
+
43
58
  ###############################################
44
59
  # TESTS
45
60
  ###############################################
@@ -77,12 +92,12 @@ gemspec = Gem::Specification.new do |t|
77
92
  t.has_rdoc = true
78
93
  t.authors = ["John Prince"]
79
94
  t.files = dist_files
80
- t.add_dependency 'axml', '~> 0.0.5'
95
+ t.add_dependency 'ms-core'
96
+ t.add_dependency 'nokogiri'
81
97
  t.add_dependency 'runarray'
82
98
  t.rdoc_options = rdoc_options
83
99
  t.extra_rdoc_files = rdoc_extra_includes
84
100
  t.executables = FL["bin/*"].map {|file| File.basename(file) }
85
- t.requirements << 'xmlparser (preferrably) or libxml'
86
101
  t.test_files = FL["spec/**/*_spec.rb"]
87
102
  end
88
103
 
@@ -1,5 +1,6 @@
1
+ #!/usr/bin/ruby
1
2
 
2
- require 'ms/spectrum'
3
+ require 'ms/data/lazy_string'
3
4
 
4
5
  if ARGV.size == 0
5
6
  puts "usage: #{File.basename(__FILE__)} <base64_string>"
@@ -7,7 +8,5 @@ if ARGV.size == 0
7
8
  exit
8
9
  end
9
10
 
10
- precision = 32
11
- network_order = true
12
- ar = Ms::Spectrum.base64_to_array(ARGV.shift, precision, network_order)
11
+ ar = Ms::Data::LazyString.new(ARGV.shift).to_a
13
12
  puts "[ " + ar.join(", ") + " ]"
data/bin/ms_to_obiwarp.rb CHANGED
@@ -21,8 +21,8 @@ opts = OptionParser.new do |op|
21
21
  op.separator "(sums m/z values that round to the same bin)"
22
22
  op.separator ""
23
23
  op.on("--mz-inc N", Float, "m/z increment (def: 1.0)") {|n| opt[:mz_inc] = n.to_f}
24
- op.on("--mz-start N", Float, "m/z start (def: start of 1st full scan)") {|n| opt[:start_mz] = n.to_f}
25
- op.on("--mz-end N", Float, "m/z end (def: end of 1st full scan)") {|n| opt[:end_mz] = n.to_f}
24
+ op.on("--mz-start N", Float, "m/z start (otherwise auto)") {|n| opt[:start_mz] = n.to_f}
25
+ op.on("--mz-end N", Float, "m/z end (otherwise auto)") {|n| opt[:end_mz] = n.to_f}
26
26
  op.on("--baseline N", Float, "value for missing indices (def: #{opt[:baseline]})") {|n| opt[:baseline] = n.to_f}
27
27
  op.on("--ascii", "generates an lmata file instead") {opt[:ascii] = true}
28
28
  op.on("-v", "--verbose") {$VERBOSE = true}
@@ -36,21 +36,12 @@ end
36
36
  ARGV.each do |file|
37
37
  Ms::Msrun.open(file) do |msrun|
38
38
  mslevel = 1
39
- (start_mz, end_mz) = msrun.start_and_end_mz(mslevel)
40
- (times, spectra) = msrun.times_and_spectra(mslevel)
41
- args = {
42
- :start_mz => start_mz,
43
- :end_mz => end_mz,
44
-
45
- :start_tm => times.first,
46
- :end_tm => times.last,
47
- :inc_tm => nil,
48
- }
49
- args.merge!(opt)
50
- lmat = Ms::Msrun::Lmat.new.from_times_and_spectra(times, spectra, args)
39
+ lmat = Lmat.new
40
+ t = Time.now
41
+ lmat.from_msrun(msrun, opt)
51
42
  ext = File.extname(file)
52
43
  outfile = file.sub(/#{Regexp.escape(ext)}$/, opt[:newext])
53
- if args[:ascii]
44
+ if opt[:ascii]
54
45
  outfile << "a"
55
46
  lmat.print(outfile)
56
47
  else
data/bin/ms_to_search.rb CHANGED
@@ -1,37 +1,30 @@
1
1
  #!/usr/bin/ruby
2
2
 
3
3
  require 'rubygems'
4
- require 'tap'
4
+ require 'optparse'
5
+ require 'ms/msrun/search'
5
6
 
7
+ opts = OptionParser.new do |op|
8
+ op.banner = "usage: #{File.basename(__FILE__)} <file>.mzXML ..."
9
+ op.separator "outputs: <file>.mgf"
10
+ op.separator "[by default outputs .mgf file]"
6
11
 
7
- module Ms ; end
8
- class Ms::Msrun ; end
9
-
10
- # Documentation here
11
- class Ms::Msrun::Search < Tap::Task
12
-
13
- #config :first_scan, 0, :short => 'F', &c.integer # first scan
14
- #config :last_scan, 1e12, :short => 'L', &c.integer # last scan
15
- ## if not determined to be +1, then create these charge states
16
- #config( :charge_states, [2,3], :short => 'c') {|v| v.split(',') }
17
- #config :bottom_mh, 0, :short => 'B', &c.float # bottom MH+
18
- #config :top_mh, -1.0, :short => 'T', &c.float # top MH+
19
- #config :min_peaks, 0, :short => 'P', &c.integer # minimum peak count
20
- #config :ms_levels, 2..-1, :short => 'M', &c.range # ms levels to export
12
+ end
21
13
 
14
+ if ARGV.size == 0
15
+ puts opts.to_s
16
+ exit
17
+ end
22
18
 
23
- def process(filename)
24
- Ms::Msrun.open(filename) do |ms|
25
- ms.to_mgf(ms.filename.chomp(File.extname(ms.filename)))
26
- end
19
+ ARGV.each do |file|
20
+ Ms::Msrun.open(file) do |ms|
21
+ outfile = ms.to_mgf(file.sub(/\.mzxml/i, '.mgf'))
22
+ ms.to_mgf(outfile)
27
23
  end
28
24
  end
29
25
 
30
- Ms::Msrun::Search.execute
31
-
32
26
  # extract_msn.exe -M0.2 -B85 -T4500 -S0 -G1 -I35 -C0 -P2 -D output smallraw.RAW
33
27
 
34
-
35
28
  #config :group_mass_tol, 1.4, :short => 'M', &c.float # prec. mass tolerance for grouping
36
29
  #config :bottom_mw, 0.0, :short => 'B', &c.float # bottom MW for data file creation
37
30
  #config :top_mw, 999999.0, :short => 'T', &c.float # top MW for data file creation
data/lib/lmat.rb CHANGED
@@ -64,49 +64,65 @@ class Lmat
64
64
  self
65
65
  end
66
66
 
67
- # converts raw times and spectrum to a labeled matrix
68
- # times is an array (or VecI object)
69
- # where each row = [mz,inten,mz,inten...]
67
+ # converts msrun object to a labeled matrix
70
68
  # takes hash with symbols as keys
71
- # if inc_tm is undefined, then times from the times array will be used
72
- def from_times_and_spectra(times, spectra, args)
69
+ def from_msrun(msrun, args)
73
70
  opt = {
74
- :start_mz => 400.0,
75
- :end_mz => 1500.0,
76
71
  :inc_mz => 1.0,
77
72
  :behave_mz => 'sum',
78
-
79
- :start_tm => 0.0,
80
- :end_tm => 3600.0,
81
- :inc_tm => nil,
82
-
83
73
  :baseline=> 0.0,
74
+
75
+ #:start_tm => 0.0,
76
+ #:end_tm => 3600.0,
77
+ #:inc_tm => nil,
78
+
79
+ #:start_mz => 400.0,
80
+ #:end_mz => 1500.0,
84
81
  }
85
82
  opt.merge!(args)
86
- unless opt[:start_tm] then opt[:start_tm] = times.first end
87
- unless opt[:end_tm] then opt[:end_tm] = times.last end
88
-
83
+
84
+ (st, en) = msrun.start_and_end_mz
85
+ unless st && en
86
+ msg = ["scanning spectrum for start and end m/z values"]
87
+ msg << "(use :start_mz and :end_mz options to avoid this)"
88
+ warn msg
89
+ (st, en) = msrun.start_and_end_mz_brute_force
90
+ end
91
+ opt[:start_mz] ||= st
92
+ opt[:end_mz] ||= en
93
+
94
+ #unless opt[:start_tm] then opt[:start_tm] = times.first end
95
+ #unless opt[:end_tm] then opt[:end_tm] = times.last end
96
+
89
97
  if opt[:inc_tm]
90
98
  raise NotImplementedError, "haven't implemented interpolation in ruby yet! (#{File.basename(__FILE__)}: #{__LINE__})"
91
99
  else ## No interpolation
92
- if times.first != opt[:start_tm] || times.last != opt[:end_tm]
93
- abort "haven't implemented yet! (#{File.basename(__FILE__)}: #{__LINE__})"
94
- else
95
- @mvec = NArray.new(times)
96
- give_vecs = true
97
- vecs = spectra.map do |spectrum|
98
- #(mz,inten) = spectrum_to_mz_and_inten(spectrum, VecD)
99
- # TODO: Figure out a shallow copy here:
100
- # perhaps we'll make spectra Vec objects by default in future and
101
- # then we'd be set...
102
- mzs = NArray.new(spectrum.mzs)
103
- intens = NArray.new(spectrum.intensities)
104
- (x,y) = mzs.inc_x(intens, opt[:start_mz], opt[:end_mz], opt[:inc_mz], opt[:baseline], opt[:behave_mz])
105
- @nvec = x # ends up being the last one, but that's OK
106
- y
100
+ times = []
101
+ @nvec = nil
102
+ vecs = []
103
+ num_scans = msrun.scan_count
104
+ printf "Reading #{num_scans} spectra [.=100]" if $VERBOSE
105
+ spectrum_cnt = 0
106
+ msrun.each do |scan|
107
+ spectrum = scan.spectrum
108
+ times << scan.time
109
+ #(mz,inten) = spectrum_to_mz_and_inten(spectrum, VecD)
110
+ # TODO: Figure out a shallow copy here:
111
+ # perhaps we'll make spectra Vec objects by default in future and
112
+ # then we'd be set...
113
+ mzs = NArray.new(spectrum.mzs)
114
+ intens = NArray.new(spectrum.intensities)
115
+ (x,y) = mzs.inc_x(intens, opt[:start_mz], opt[:end_mz], opt[:inc_mz], opt[:baseline], opt[:behave_mz])
116
+ spectrum_cnt += 1
117
+ if spectrum_cnt % 100 == 0
118
+ printf "." if $VERBOSE ; $stdout.flush
107
119
  end
108
- @mat = vecs
120
+ @nvec ||= x # just need the first one for the x values
121
+ vecs << y
109
122
  end
123
+ puts "DONE!" if $VERBOSE
124
+ @mvec = NArray.new(times)
125
+ @mat = vecs
110
126
  end
111
127
  self
112
128
  end
@@ -124,28 +140,6 @@ class Lmat
124
140
  other != nil && self.class == other.class && @nvec == other.nvec && @mvec == other.mvec && @mat == other.mat
125
141
  end
126
142
 
127
- # converts a single array of alternating m/z intensity values to two
128
- # separate arrays
129
- # (maybe implement in Ruby::Inline?)
130
- # the answer is given in terms of arrs_as (object of class "arrs_as" must
131
- # respond to "[]" and create a certain sized array with arrs_as.new(size))
132
- def spectrum_to_mz_and_inten(spectrum, arrs_as=Array)
133
- half_size = spectrum.size / 2
134
- mzs = arrs_as.new(half_size)
135
- intens = arrs_as.new(half_size)
136
- mz = true
137
- spectrum.each_index do |i|
138
- if mz
139
- mzs[i/2] = spectrum[i]
140
- mz = false
141
- else
142
- mz = true
143
- intens[(i-1)/2] = spectrum[i]
144
- end
145
- end
146
- [mzs, intens]
147
- end
148
-
149
143
  def write(file=nil)
150
144
  handle = $>
151
145
  if file; handle = File.open(file, "wb") end
data/lib/ms/msrun.rb CHANGED
@@ -3,9 +3,15 @@ require 'ms/scan'
3
3
  require 'ms/precursor'
4
4
  require 'ms/spectrum'
5
5
  require 'ms/msrun/search'
6
+ require 'ms/msrun/index'
6
7
 
7
8
  module Ms; end
8
9
  class Ms::Msrun
10
+ include Enumerable
11
+
12
+ #DEFAULT_PARSER = 'axml'
13
+ #DEFAULT_PARSER = 'regexp'
14
+ DEFAULT_PARSER = 'nokogiri'
9
15
 
10
16
  # the retention time in seconds of the first scan (regardless of any
11
17
  # meta-data written in the header)
@@ -13,8 +19,6 @@ class Ms::Msrun
13
19
  # the retention time in seconds of the last scan (regardless of any
14
20
  # meta-data written in the header)
15
21
  attr_accessor :end_time
16
- # an array of scans
17
- attr_accessor :scans
18
22
 
19
23
  # The filetype. Valid types (for parsing) are:
20
24
  # :mzxml
@@ -39,6 +43,15 @@ class Ms::Msrun
39
43
  # this will be nil.
40
44
  attr_accessor :parent_location
41
45
 
46
+ # an array of doublets, [start_byte, length] for each scan element
47
+ attr_accessor :index
48
+
49
+ # holds the class that parses the file
50
+ attr_accessor :parser
51
+
52
+ # an array holding the scan numbers found in the run
53
+ attr_accessor :scan_nums
54
+
42
55
  # Opens the filename
43
56
  def self.open(filename, &block)
44
57
  File.open(filename) {|io| block.call( self.new(io, filename) ) }
@@ -50,10 +63,13 @@ class Ms::Msrun
50
63
  def initialize(io, filename=nil)
51
64
  @scan_counts = nil
52
65
  @filename = filename
53
- @filetype, @version = Utils.filetype_and_version(io)
54
- parser = Utils.get_parser(@filetype, @version)
55
- parser.new.parse(self, io, @version)
56
- @scan_counts = nil # <- to keep warnings away
66
+ @filetype, @version = Ms::Msrun.filetype_and_version(io)
67
+ parser_klass = Ms::Msrun.get_parser(@filetype, @version)
68
+
69
+ @parser = parser_klass.new(self, io, @version)
70
+ @index = Ms::Msrun::Index.new(io)
71
+ @scan_nums = @index.scan_nums
72
+ @parser.parse_header(@index.header_length)
57
73
  end
58
74
 
59
75
  def parent_basename_noext
@@ -61,32 +77,59 @@ class Ms::Msrun
61
77
  end
62
78
 
63
79
  # returns each scan
64
- def each(&block)
65
- scans.each(&block)
80
+ # options:
81
+ # :spectrum => true | false (default is true)
82
+ # :precursor => true | false (default is true)
83
+ # :ms_level => Integer or Array return only scans of that level
84
+ # :reverse => true | false (default is false) goes backwards
85
+ def each_scan(parse_opts={}, &block)
86
+ ms_levels =
87
+ if msl = parse_opts[:ms_level]
88
+ if msl.is_a?(Integer) ; [msl]
89
+ else ; msl
90
+ end
91
+ end
92
+ snums = @index.scan_nums
93
+ snums = snums.reverse if parse_opts[:reverse]
94
+ snums.each do |scan_num|
95
+ if ms_levels
96
+ next unless ms_levels.include?(ms_level(scan_num))
97
+ end
98
+ block.call(scan(scan_num, parse_opts))
99
+ end
66
100
  end
101
+ alias_method :each, :each_scan
67
102
 
68
103
  # opens the file and yields each scan in the block
69
- def self.foreach(filename, &block)
104
+ # see each_scan for parsing options
105
+ def self.foreach(filename, parse_opts={}, &block)
70
106
  self.open(filename) do |obj|
71
- obj.each(&block)
107
+ obj.each_scan(parse_opts, &block)
72
108
  end
73
109
  end
74
110
 
75
- def scans_by_ms_level
76
- by_level = []
77
- scans.each do |scan|
78
- by_level[scan.ms_level] = scan
79
- end
80
- by_level
111
+ # a very fast method to only query the ms_level of a scan
112
+ def ms_level(num)
113
+ @parser.parse_ms_level(@index[num].first, @index[num].last)
81
114
  end
82
115
 
116
+ # returns a Ms::Scan object for the scan at that number
117
+ #
118
+ def scan(num, parse_opts={})
119
+ #@parser.parse_scan(*(@index[num]), parse_opts)
120
+ @parser.parse_scan(@index[num].first, @index[num].last, parse_opts)
121
+ end
122
+
123
+ #bracket_method = '[]'.to_sym
124
+ #alias_method bracket_method, :scan
125
+
83
126
  # returns an array, whose indices provide the number of scans in each index level the ms_levels, [0] = all the scans, [1] = mslevel 1, [2] = mslevel 2,
84
127
  # ...
85
128
  def scan_counts
86
129
  return @scan_counts if @scan_counts
87
130
  ar = []
88
131
  ar[0] = 0
89
- scans.each do |sc|
132
+ each_scan do |sc|
90
133
  level = sc.ms_level
91
134
  unless ar[level]
92
135
  ar[level] = 0
@@ -104,113 +147,57 @@ class Ms::Msrun
104
147
  if mslevel == 0
105
148
  @scan_count
106
149
  else
107
- num = 0
108
- scans.each do |sc|
109
- if sc.ms_level == mslevel
110
- num += 1
111
- end
112
- end
113
- num
150
+ scan_counts[mslevel]
114
151
  end
115
152
  end
116
153
  end
117
154
 
118
- # for level 1, finds first scan and asks if it has start_mz/end_mz
119
- # attributes. for other levels, asks for start_mz/ end_mz and takes the
120
- # min/max. If start_mz and end_mz are not found, goes through every scan
121
- # finding the max/min first and last m/z. returns [start_mz (rounded down to
122
- # nearest int), end_mz (rounded up to nearest int)]
123
- def start_and_end_mz(mslevel=1)
124
- if mslevel == 1
125
- # special case for mslevel 1 (where we expect scans to be same length)
126
- scans.each do |sc|
127
- if sc.ms_level == mslevel
128
- if sc.start_mz && sc.end_mz
129
- return [sc.start_mz, sc.end_mz]
130
- end
131
- break
132
- end
133
- end
134
- end
135
- hi_mz = nil
136
- lo_mz = nil
137
- # see if we have start_mz and end_mz for the level we want
138
- # set the initial hi_mz and lo_mz in any case
139
- have_start_end_mz = false
140
- scans.each do |sc|
141
- if sc.ms_level == mslevel
142
- if sc.start_mz && sc.end_mz
143
- lo_mz = sc.start_mz
144
- hi_mz = sc.end_mz
145
- else
146
- mz_ar = sc.spectrum.mzs
147
- hi_mz = mz_ar.last
148
- lo_mz = mz_ar.first
149
- end
150
- break
151
- end
152
- end
153
- if have_start_end_mz
154
- scans.each do |sc|
155
- if sc.ms_level == mslevel
156
- if sc.start_mz < lo_mz
157
- lo_mz = sc.start_mz
158
- end
159
- if sc.end_mz > hi_mz
160
- hi_mz = sc.end_mz
161
- end
162
- end
155
+
156
+ # returns [start_mz, end_mz] or [nil,nil] if unknown
157
+ def start_and_end_mz
158
+ scan = first(:ms_level => 1, :spectrum => false, :precursor => false)
159
+ [scan.start_mz, scan.end_mz]
160
+ end
161
+
162
+ # goes through every scan and gets the first and last m/z, then returns the
163
+ # max.ceil and min.floor
164
+ def start_and_end_mz_brute_force
165
+ first_scan = first(:ms_level => 1, :precursor => false)
166
+ first_mzs = first_scan.spectrum.mzs
167
+
168
+ lo_mz = first_mzs[0]
169
+ hi_mz = first_mzs[-1]
170
+
171
+ each_scan(:ms_level => 1, :precursor => false) do |sc|
172
+ mz_ar = sc.spectrum.mzs
173
+ if mz_ar.last > hi_mz
174
+ hi_mz = mz_ar.last
163
175
  end
164
- else
165
- # didn't have the attributes (find by brute force)
166
- scans.each do |sc|
167
- if sc.ms_level == mslevel
168
- mz_ar = sc.spectrum.mzs
169
- if mz_ar.last > hi_mz
170
- hi_mz = mz_ar.last
171
- end
172
- if mz_ar.last < lo_mz
173
- lo_mz = mz_ar.last
174
- end
175
- end
176
+ if mz_ar.last < lo_mz
177
+ lo_mz = mz_ar.last
176
178
  end
177
179
  end
178
180
  [lo_mz.floor, hi_mz.ceil]
179
181
  end
180
182
 
181
- # returns an array of times and parallel array of spectra objects.
182
- # ms_level = 0 then all spectra and times
183
- # ms_level = 1 then all spectra of ms_level 1
184
- def times_and_spectra(ms_level=0)
185
- spectra = []
186
- if ms_level == 0
187
- times = @scans.map do |scan|
188
- spectra << scan.spectrum
189
- scan.time
190
- end
191
- [times, spectra]
192
- else # choose a particular ms_level
193
- times = []
194
- @scans.each do |scan|
195
- if ms_level == scan.ms_level
196
- spectra << scan.spectrum
197
- times << scan.time
198
- end
199
- end
200
- [times, spectra]
183
+ def first(opts={})
184
+ the_first = nil
185
+ each_scan(opts) do |scan|
186
+ the_first = scan
187
+ break
201
188
  end
189
+ the_first
202
190
  end
203
- end
204
-
205
191
 
206
- module Ms::Msrun::Axml ; end # so we can get our parser
207
-
208
- module Ms::Msrun::Utils
192
+ def last(opts={})
193
+ opts[:reverse] = true
194
+ first(opts)
195
+ end
209
196
 
210
197
  def self.get_parser(filetype, version)
211
- require "ms/msrun/axml/#{filetype}"
198
+ require "ms/msrun/#{DEFAULT_PARSER}/#{filetype}"
212
199
  parser_class = filetype.to_s.capitalize
213
- base_class = Ms::Msrun::Axml
200
+ base_class = Ms::Msrun.const_get( DEFAULT_PARSER.capitalize )
214
201
  if base_class.const_defined? parser_class
215
202
  base_class.const_get parser_class
216
203
  else
@@ -247,6 +234,7 @@ module Ms::Msrun::Utils
247
234
  end
248
235
  end
249
236
 
237
+
250
238
  Mzxml_regexp = /http:\/\/sashimi.sourceforge.net\/schema(_revision)?\/([\w\d_\.]+)/o
251
239
  # 'http://sashimi.sourceforge.net/schema/MsXML.xsd' # version 1
252
240
  # 'http://sashimi.sourceforge.net/schema_revision/mzXML_X.X' # others
@@ -258,6 +246,7 @@ module Ms::Msrun::Utils
258
246
  if file_or_io.is_a? IO
259
247
  io = file_or_io
260
248
  found = nil
249
+ io.rewind
261
250
  # Test for RAW file:
262
251
  header = io.read(18).unpack(Raw_header_unpack_code).join
263
252
  if header == 'Finnigan'
@@ -294,4 +283,5 @@ module Ms::Msrun::Utils
294
283
  end
295
284
  end
296
285
  end
286
+
297
287
  end