QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsfileutils.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsfileutils.cpp
3  ---------------------
4  begin : November 2017
5  copyright : (C) 2017 by Etienne Trimaille
6  email : etienne.trimaille at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsfileutils.h"
16 #include "qgis.h"
17 #include <QObject>
18 #include <QRegularExpression>
19 #include <QFileInfo>
20 #include <QDir>
21 #include <QSet>
22 #include <QDirIterator>
23 
24 QString QgsFileUtils::representFileSize( qint64 bytes )
25 {
26  QStringList list;
27  list << QObject::tr( "KB" ) << QObject::tr( "MB" ) << QObject::tr( "GB" ) << QObject::tr( "TB" );
28 
29  QStringListIterator i( list );
30  QString unit = QObject::tr( "bytes" );
31 
32  while ( bytes >= 1024.0 && i.hasNext() )
33  {
34  unit = i.next();
35  bytes /= 1024.0;
36  }
37  return QStringLiteral( "%1 %2" ).arg( QString::number( bytes ), unit );
38 }
39 
40 QStringList QgsFileUtils::extensionsFromFilter( const QString &filter )
41 {
42  const QRegularExpression rx( QStringLiteral( "\\*\\.([a-zA-Z0-9]+)" ) );
43  QStringList extensions;
44  QRegularExpressionMatchIterator matches = rx.globalMatch( filter );
45 
46  while ( matches.hasNext() )
47  {
48  const QRegularExpressionMatch match = matches.next();
49  if ( match.hasMatch() )
50  {
51  QStringList newExtensions = match.capturedTexts();
52  newExtensions.pop_front(); // remove whole match
53  extensions.append( newExtensions );
54  }
55  }
56  return extensions;
57 }
58 
59 QString QgsFileUtils::ensureFileNameHasExtension( const QString &f, const QStringList &extensions )
60 {
61  if ( extensions.empty() || f.isEmpty() )
62  return f;
63 
64  QString fileName = f;
65  bool hasExt = false;
66  for ( const QString &extension : qgis::as_const( extensions ) )
67  {
68  const QString extWithDot = extension.startsWith( '.' ) ? extension : '.' + extension;
69  if ( fileName.endsWith( extWithDot, Qt::CaseInsensitive ) )
70  {
71  hasExt = true;
72  break;
73  }
74  }
75 
76  if ( !hasExt )
77  {
78  const QString extension = extensions.at( 0 );
79  const QString extWithDot = extension.startsWith( '.' ) ? extension : '.' + extension;
80  fileName += extWithDot;
81  }
82 
83  return fileName;
84 }
85 
86 QString QgsFileUtils::addExtensionFromFilter( const QString &fileName, const QString &filter )
87 {
88  const QStringList extensions = extensionsFromFilter( filter );
89  return ensureFileNameHasExtension( fileName, extensions );
90 }
91 
92 QString QgsFileUtils::stringToSafeFilename( const QString &string )
93 {
94  QRegularExpression rx( QStringLiteral( "[/\\\\\\?%\\*\\:\\|\"<>]" ) );
95  QString s = string;
96  s.replace( rx, QStringLiteral( "_" ) );
97  return s;
98 }
99 
100 QString QgsFileUtils::findClosestExistingPath( const QString &path )
101 {
102  if ( path.isEmpty() )
103  return QString();
104 
105  QDir currentPath;
106  QFileInfo fi( path );
107  if ( fi.isFile() )
108  currentPath = fi.dir();
109  else
110  currentPath = QDir( path );
111 
112  QSet< QString > visited;
113  while ( !currentPath.exists() )
114  {
115  const QString parentPath = QDir::cleanPath( currentPath.absolutePath() + QStringLiteral( "/.." ) );
116  if ( visited.contains( parentPath ) )
117  return QString(); // break circular links
118 
119  if ( parentPath.isEmpty() || parentPath == QLatin1String( "." ) )
120  return QString();
121  currentPath = QDir( parentPath );
122  visited << parentPath;
123  }
124 
125  const QString res = QDir::cleanPath( currentPath.absolutePath() );
126 
127  if ( res == QDir::currentPath() )
128  return QString(); // avoid default to binary folder if a filename alone is specified
129 
130  return res == QLatin1String( "." ) ? QString() : res;
131 }
132 
133 QStringList QgsFileUtils::findFile( const QString &file, const QString &basePath, int maxClimbs, int searchCeilling, const QString &currentDir )
134 {
135  int depth = 0;
136  QString originalFolder;
137  QDir folder;
138  const QString fileName( basePath.isEmpty() ? QFileInfo( file ).fileName() : file );
139  const QString baseFolder( basePath.isEmpty() ? QFileInfo( file ).path() : basePath );
140 
141  if ( QFileInfo( baseFolder ).isDir() )
142  {
143  folder = QDir( baseFolder ) ;
144  originalFolder = folder.absolutePath();
145  }
146  else // invalid folder or file path
147  {
148  folder = QDir( QFileInfo( baseFolder ).absolutePath() );
149  originalFolder = folder.absolutePath();
150  }
151 
152  QStringList searchedFolder = QStringList();
153  QString existingBase;
154  QString backupDirectory = QDir::currentPath();
155  QStringList foundFiles;
156 
157  if ( !currentDir.isEmpty() && backupDirectory != currentDir && QDir( currentDir ).exists() )
158  QDir::setCurrent( currentDir );
159 
160  // find the nearest existing folder
161  while ( !folder.exists() && folder.absolutePath().count( '/' ) > searchCeilling )
162  {
163 
164  existingBase = folder.path();
165  if ( !folder.cdUp() )
166  folder = QFileInfo( existingBase ).absoluteDir(); // using fileinfo to move up one level
167 
168  depth += 1;
169 
170  if ( depth > ( maxClimbs + 4 ) ) //break early when no folders can be found
171  break;
172  }
173  bool folderExists = folder.exists();
174 
175  if ( depth > maxClimbs )
176  maxClimbs = depth;
177 
178  if ( folder.absolutePath().count( '/' ) < searchCeilling )
179  searchCeilling = folder.absolutePath().count( '/' ) - 1;
180 
181  while ( depth <= maxClimbs && folderExists && folder.absolutePath().count( '/' ) >= searchCeilling )
182  {
183 
184  QDirIterator localFinder( folder.path(), QStringList() << fileName, QDir::Files, QDirIterator::NoIteratorFlags );
185  searchedFolder.append( folder.absolutePath() );
186  if ( localFinder.hasNext() )
187  {
188  foundFiles << localFinder.next();
189  return foundFiles;
190  }
191 
192 
193  const QFileInfoList subdirs = folder.entryInfoList( QDir::AllDirs );
194  for ( const QFileInfo &subdir : subdirs )
195  {
196  if ( ! searchedFolder.contains( subdir.absolutePath() ) )
197  {
198  QDirIterator subDirFinder( subdir.path(), QStringList() << fileName, QDir::Files, QDirIterator::Subdirectories );
199  if ( subDirFinder.hasNext() )
200  {
201  QString possibleFile = subDirFinder.next();
202  if ( !subDirFinder.hasNext() )
203  {
204  foundFiles << possibleFile;
205  return foundFiles;
206  }
207 
208  foundFiles << possibleFile;
209  while ( subDirFinder.hasNext() )
210  {
211  foundFiles << subDirFinder.next();
212  }
213  return foundFiles;
214  }
215  }
216  }
217  depth += 1;
218 
219  if ( depth > maxClimbs )
220  break;
221 
222  folderExists = folder.cdUp();
223  }
224 
225  if ( QDir::currentPath() == currentDir && currentDir != backupDirectory )
226  QDir::setCurrent( backupDirectory );
227 
228  return foundFiles;
229 }
230 
QgsFileUtils::extensionsFromFilter
static QStringList extensionsFromFilter(const QString &filter)
Returns a list of the extensions contained within a file filter string.
Definition: qgsfileutils.cpp:40
qgis.h
QgsFileUtils::findClosestExistingPath
static QString findClosestExistingPath(const QString &path)
Returns the top-most existing folder from path.
Definition: qgsfileutils.cpp:100
qgsfileutils.h
QgsFileUtils::ensureFileNameHasExtension
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
Definition: qgsfileutils.cpp:59
QgsFileUtils::representFileSize
static QString representFileSize(qint64 bytes)
Returns the human size from bytes.
Definition: qgsfileutils.cpp:24
QgsFileUtils::findFile
static QStringList findFile(const QString &file, const QString &basepath=QString(), int maxClimbs=4, int searchCeiling=4, const QString &currentDir=QString())
Will check basepath in an outward spiral up to maxClimbs levels to check if file exists.
Definition: qgsfileutils.cpp:133
QgsFileUtils::stringToSafeFilename
static QString stringToSafeFilename(const QString &string)
Converts a string to a safe filename, replacing characters which are not safe for filenames with an '...
Definition: qgsfileutils.cpp:92
QgsFileUtils::addExtensionFromFilter
static QString addExtensionFromFilter(const QString &fileName, const QString &filter)
Ensures that a fileName ends with an extension from the specified filter string.
Definition: qgsfileutils.cpp:86