Skip to content
Snippets Groups Projects
FileDescriptor.cpp 5.39 KiB
Newer Older
#include "MantidKernel/FileDescriptor.h"

#include <Poco/File.h>
#include <Poco/Path.h>

#include <stdexcept>
namespace Mantid {
namespace Kernel {
//----------------------------------------------------------------------------------------------
// Public static methods
//----------------------------------------------------------------------------------------------
/**
 * Check if the given file appears to be an ASCII file. The check searches the
 * first nbytes of the file and returns false if a non-ascii character is found.
 * If the file is shorter than nbytes then it checks until the end of the
 * stream.
 * @param filename A string pointing to an existing file
 * @param nbytes The number of bytes of the file to check (Default=256)
 * @returns True if the file is considered ASCII, false otherwise
 * @throws std::invalid_argument if the file cannot be opened
 * @throws std::runtime_error if an error is occurred while reading the stream
 */
bool FileDescriptor::isAscii(const std::string &filename, const size_t nbytes) {
  std::ifstream data(filename.c_str(), std::ios::in | std::ios::binary);
  if (!data) {
    throw std::invalid_argument(
        "FileDescriptor::isAscii() - Unable to open file '" + filename + "'");
  }
  return FileDescriptor::isAscii(data, nbytes);
}

/**
 * Check if the given stream appears to point to an ASCII data. The check
 * searches the
 * next nbytes of the stream and returns false if a non-ascii character is
 * found.
 * If the stream is shorter than nbytes or a reading error occurs then it simply
 * returns
 * the result up to that point
 * The stream is reset to the position is was at when entering the function
 * @param data An input stream opened in binary mode
 * @param nbytes The number of bytes of the file to check (Default=256)
 * @returns True if the stream is considered ASCII, false otherwise
 */
bool FileDescriptor::isAscii(std::istream &data, const size_t nbytes) {
  const std::streampos startPos = data.tellg();
  char byte('\0');
  size_t counter(0);
  bool result(true);
  while (counter < nbytes) {
    data >> byte;
    if (!data) // reading error
      data.clear();
      break;
    unsigned long ch = static_cast<unsigned long>(byte);
    if (!(ch <= 0x7F)) // non-ascii
      result = false;
      break;
    ++counter;
  }

  data.seekg(startPos);
  return result;
}

/**
* Check if a file is a text file
* @param file :: The file pointer
* @param nbytes The number of bytes of the file to check (Default=256)
* @returns true if the file an ascii text file, false otherwise
*/
bool FileDescriptor::isAscii(FILE *file, const size_t nbytes) {
  // read the data and reset the seek index back to the beginning
  auto data = new char[nbytes];
  char *pend = &data[fread(data, 1, nbytes, file)];
  int retval = fseek(file, 0, SEEK_SET);
  if (retval < 0)
    throw std::runtime_error("FileDescriptor::isAscii - Cannot change position "
                             "to the beginning of the file with fseek");

  // Call it a binary file if we find a non-ascii character in the
  // first nbytes bytes of the file.
  bool result = true;
  for (char *p = data; p < pend; ++p) {
    unsigned long ch = static_cast<unsigned long>(*p);
    if (!(ch <= 0x7F)) {
      result = false;
      break;
  }
  delete[] data;

  return result;
}

//----------------------------------------------------------------------------------------------
// Public methods
//----------------------------------------------------------------------------------------------

/**
 * @param filename A string containing a filename. The file must exist
 * @throws std::invalid_argument if the filename is empty or the file does not
 * exist
 */
FileDescriptor::FileDescriptor(const std::string &filename)
    : m_filename(), m_extension(), m_file(), m_ascii(false) {
  if (filename.empty()) {
    throw std::invalid_argument("FileDescriptor() - Empty filename '" +
                                filename + "'");
  }
  if (!Poco::File(filename).exists()) {
    throw std::invalid_argument("FileDescriptor() - File '" + filename +
                                "' does not exist");
  }
  initialize(filename);
}

/**
 * Closes the file handle
 */
FileDescriptor::~FileDescriptor() { m_file.close(); }

/**
 * Moves the stream pointer back to the start of the file, without
 * reopening the file. Note that this will affect the stream that
 * has been accessed using the stream() method
 */
void FileDescriptor::resetStreamToStart() {
  m_file.close(); // reset all flags
  // If the stream was closed, reopen it
  if (!m_file.is_open()) {
    m_file.open(m_filename.c_str(), std::ios::in | std::ios::binary);
  } else {
    m_file.seekg(0);
  }
}

//----------------------------------------------------------------------------------------------
// Private methods
//----------------------------------------------------------------------------------------------

/**
 * Set the description fields and opens the file
 * @param filename A string pointing to an existing file
 */
void FileDescriptor::initialize(const std::string &filename) {
  m_filename = filename;
  m_extension = "." + Poco::Path(filename).getExtension();

  m_file.open(m_filename.c_str(), std::ios::in | std::ios::binary);
  if (!m_file)
    throw std::runtime_error("FileDescriptor::initialize - Cannot open file '" +
                             filename + "' for reading");

  m_ascii = FileDescriptor::isAscii(m_file);
}

} // namespace Kernel