You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

266 lines
10 KiB

  1. // util/kaldi-io.h
  2. // Copyright 2009-2011 Microsoft Corporation; Jan Silovsky
  3. // 2016 Xiaohui Zhang
  4. // See ../../COPYING for clarification regarding multiple authors
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License");
  7. // you may not use this file except in compliance with the License.
  8. // You may obtain a copy of the License at
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  11. // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  12. // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  13. // MERCHANTABLITY OR NON-INFRINGEMENT.
  14. // See the Apache 2 License for the specific language governing permissions and
  15. // limitations under the License.
  16. #ifndef KALDI_UTIL_KALDI_IO_H_
  17. #define KALDI_UTIL_KALDI_IO_H_
  18. #ifdef _MSC_VER
  19. #include <fcntl.h>
  20. #include <io.h>
  21. #endif
  22. #include <cctype> // For isspace.
  23. #include <limits>
  24. #include <string>
  25. #include "base/kaldi-common.h"
  26. // #include "matrix/kaldi-matrix.h"
  27. namespace kaldi {
  28. class OutputImplBase; // Forward decl; defined in a .cc file
  29. class InputImplBase; // Forward decl; defined in a .cc file
  30. /// \addtogroup io_group
  31. /// @{
  32. // The Output and Input classes handle stream-opening for "extended" filenames
  33. // that include actual files, standard-input/standard-output, pipes, and
  34. // offsets into actual files. They also handle reading and writing the
  35. // binary-mode headers for Kaldi files, where applicable. The classes have
  36. // versions of the Open routines that throw and do not throw, depending whether
  37. // the calling code wants to catch the errors or not; there are also versions
  38. // that write (or do not write) the Kaldi binary-mode header that says if it's
  39. // binary mode. Generally files that contain Kaldi objects will have the header
  40. // on, so we know upon reading them whether they have the header. So you would
  41. // use the OpenWithHeader routines for these (or the constructor); but other
  42. // types of objects (e.g. FSTs) would have files without a header so you would
  43. // use OpenNoHeader.
  44. // We now document the types of extended filenames that we use.
  45. //
  46. // A "wxfilename" is an extended filename for writing. It can take three forms:
  47. // (1) Filename: e.g. "/some/filename", "./a/b/c", "c:\Users\dpovey\My
  48. // Documents\\boo"
  49. // (whatever the actual file-system interprets)
  50. // (2) Standard output: "" or "-"
  51. // (3) A pipe: e.g. "| gzip -c > /tmp/abc.gz"
  52. //
  53. //
  54. // A "rxfilename" is an extended filename for reading. It can take four forms:
  55. // (1) An actual filename, whatever the file-system can read, e.g. "/my/file".
  56. // (2) Standard input: "" or "-"
  57. // (3) A pipe: e.g. "gunzip -c /tmp/abc.gz |"
  58. // (4) An offset into a file, e.g.: "/mnt/blah/data/1.ark:24871"
  59. // [these are created by the Table and TableWriter classes; I may also write
  60. // a program that creates them for arbitrary files]
  61. //
  62. // Typical usage:
  63. // ...
  64. // bool binary;
  65. // MyObject.Write(Output(some_filename, binary).Stream(), binary);
  66. //
  67. // ... more extensive example:
  68. // {
  69. // Output ko(some_filename, binary);
  70. // MyObject1.Write(ko.Stream(), binary);
  71. // MyObject2.Write(ko.Stream(), binary);
  72. // }
  73. enum OutputType { kNoOutput, kFileOutput, kStandardOutput, kPipeOutput };
  74. /// ClassifyWxfilename interprets filenames as follows:
  75. /// - kNoOutput: invalid filenames (leading or trailing space, things that look
  76. /// like wspecifiers and rspecifiers or like pipes to read from with leading
  77. /// |.
  78. /// - kFileOutput: Normal filenames
  79. /// - kStandardOutput: The empty string or "-", interpreted as standard output
  80. /// - kPipeOutput: pipes, e.g. "| gzip -c > /tmp/abc.gz"
  81. OutputType ClassifyWxfilename(const std::string& wxfilename);
  82. enum InputType {
  83. kNoInput,
  84. kFileInput,
  85. kStandardInput,
  86. kOffsetFileInput,
  87. kPipeInput
  88. };
  89. /// ClassifyRxfilenames interprets filenames for reading as follows:
  90. /// - kNoInput: invalid filenames (leading or trailing space, things that
  91. /// look like wspecifiers and rspecifiers or pipes to write to
  92. /// with trailing |.
  93. /// - kFileInput: normal filenames
  94. /// - kStandardInput: the empty string or "-"
  95. /// - kPipeInput: e.g. "gunzip -c /tmp/abc.gz |"
  96. /// - kOffsetFileInput: offsets into files, e.g. /some/filename:12970
  97. InputType ClassifyRxfilename(const std::string& rxfilename);
  98. class Output {
  99. public:
  100. // The normal constructor, provided for convenience.
  101. // Equivalent to calling with default constructor then Open()
  102. // with these arguments.
  103. Output(const std::string& filename, bool binary, bool write_header = true);
  104. Output() : impl_(NULL) {}
  105. /// This opens the stream, with the given mode (binary or text). It returns
  106. /// true on success and false on failure. However, it will throw if something
  107. /// was already open and could not be closed (to avoid this, call Close()
  108. /// first. if write_header == true and binary == true, it writes the Kaldi
  109. /// binary-mode header ('\0' then 'B'). You may call Open even if it is
  110. /// already open; it will close the existing stream and reopen (however if
  111. /// closing the old stream failed it will throw).
  112. bool Open(const std::string& wxfilename, bool binary, bool write_header);
  113. inline bool IsOpen(); // return true if we have an open stream. Does not
  114. // imply stream is good for writing.
  115. std::ostream& Stream(); // will throw if not open; else returns stream.
  116. // Close closes the stream. Calling Close is never necessary unless you
  117. // want to avoid exceptions being thrown. There are times when calling
  118. // Close will hurt efficiency (basically, when using offsets into files,
  119. // and using the same Input object),
  120. // but most of the time the user won't be doing this directly, it will
  121. // be done in kaldi-table.{h, cc}, so you don't have to worry about it.
  122. bool Close();
  123. // This will throw if stream could not be closed (to check error status,
  124. // call Close()).
  125. ~Output();
  126. private:
  127. OutputImplBase* impl_; // non-NULL if open.
  128. std::string filename_;
  129. KALDI_DISALLOW_COPY_AND_ASSIGN(Output);
  130. };
  131. // bool binary_in;
  132. // Input ki(some_filename, &binary_in);
  133. // MyObject.Read(ki.Stream(), binary_in);
  134. //
  135. // ... more extensive example:
  136. //
  137. // {
  138. // bool binary_in;
  139. // Input ki(some_filename, &binary_in);
  140. // MyObject1.Read(ki.Stream(), &binary_in);
  141. // MyObject2.Write(ki.Stream(), &binary_in);
  142. // }
  143. // Note that to catch errors you need to use try.. catch.
  144. // Input communicates errors by throwing exceptions.
  145. // Input interprets four kinds of filenames:
  146. // (1) Normal filenames
  147. // (2) The empty string or "-", interpreted as standard output
  148. // (3) A pipe: e.g. "gunzip -c /tmp/abc.gz |"
  149. // (4) Offsets into [real] files, e.g. "/my/filename:12049"
  150. // The last one has no correspondence in Output.
  151. class Input {
  152. public:
  153. /// The normal constructor. Opens the stream in binary mode.
  154. /// Equivalent to calling the default constructor followed by Open(); then, if
  155. /// binary != NULL, it calls ReadHeader(), putting the output in "binary"; it
  156. /// throws on error.
  157. explicit Input(const std::string& rxfilename, bool* contents_binary = NULL);
  158. Input() : impl_(NULL) {}
  159. // Open opens the stream for reading (the mode, where relevant, is binary; use
  160. // OpenTextMode for text-mode, we made this a separate function rather than a
  161. // boolean argument, to avoid confusion with Kaldi's text/binary distinction,
  162. // since reading in the file system's text mode is unusual.) If
  163. // contents_binary != NULL, it reads the binary-mode header and puts it in the
  164. // "binary" variable. Returns true on success. If it returns false it will
  165. // not be open. You may call Open even if it is already open; it will close
  166. // the existing stream and reopen (however if closing the old stream failed it
  167. // will throw).
  168. inline bool Open(const std::string& rxfilename, bool* contents_binary = NULL);
  169. // As Open but (if the file system has text/binary modes) opens in text mode;
  170. // you shouldn't ever have to use this as in Kaldi we read even text files in
  171. // binary mode (and ignore the \r).
  172. inline bool OpenTextMode(const std::string& rxfilename);
  173. // Return true if currently open for reading and Stream() will
  174. // succeed. Does not guarantee that the stream is good.
  175. inline bool IsOpen();
  176. // It is never necessary or helpful to call Close, except if
  177. // you are concerned about to many filehandles being open.
  178. // Close does not throw. It returns the exit code as int32
  179. // in the case of a pipe [kPipeInput], and always zero otherwise.
  180. int32 Close();
  181. // Returns the underlying stream. Throws if !IsOpen()
  182. std::istream& Stream();
  183. // Destructor does not throw: input streams may legitimately fail so we
  184. // don't worry about the status when we close them.
  185. ~Input();
  186. private:
  187. bool OpenInternal(const std::string& rxfilename, bool file_binary,
  188. bool* contents_binary);
  189. InputImplBase* impl_;
  190. KALDI_DISALLOW_COPY_AND_ASSIGN(Input);
  191. };
  192. template <class C>
  193. void ReadKaldiObject(const std::string& filename, C* c) {
  194. bool binary_in;
  195. Input ki(filename, &binary_in);
  196. c->Read(ki.Stream(), binary_in);
  197. }
  198. // Specialize the template for reading matrices, because we want to be able to
  199. // support reading 'ranges' (row and column ranges), like foo.mat[10:20].
  200. // template <> void ReadKaldiObject(const std::string &filename,
  201. // Matrix<float> *m);
  202. //
  203. //
  204. // template <> void ReadKaldiObject(const std::string &filename,
  205. // Matrix<double> *m);
  206. template <class C>
  207. inline void WriteKaldiObject(const C& c, const std::string& filename,
  208. bool binary) {
  209. Output ko(filename, binary);
  210. c.Write(ko.Stream(), binary);
  211. }
  212. /// PrintableRxfilename turns the rxfilename into a more human-readable
  213. /// form for error reporting, i.e. it does quoting and escaping and
  214. /// replaces "" or "-" with "standard input".
  215. std::string PrintableRxfilename(const std::string& rxfilename);
  216. /// PrintableWxfilename turns the wxfilename into a more human-readable
  217. /// form for error reporting, i.e. it does quoting and escaping and
  218. /// replaces "" or "-" with "standard output".
  219. std::string PrintableWxfilename(const std::string& wxfilename);
  220. /// @}
  221. } // end namespace kaldi.
  222. #include "util/kaldi-io-inl.h"
  223. #endif // KALDI_UTIL_KALDI_IO_H_