file.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * 文件名: file.c
  3. * 目标: 关于文件读取的实用函数
  4. */
  5. #include <sys/stat.h>
  6. #include <cctype>
  7. #include <cstdio>
  8. #include <cstdlib>
  9. #include "tool.h"
  10. #include "file.h"
  11. #include "path.h"
  12. #include "str.h"
  13. #include "log.h"
  14. #include "stdio_.h"
  15. using namespace aFuntool;
  16. #ifdef aFunWIN32_NO_CYGWIN
  17. #ifdef _MSC_VER
  18. #pragma warning(disable : 5105) // 关闭 5105 的警告输出 (Windows.h中使用)
  19. #endif
  20. #include <Windows.h>
  21. #else
  22. #include <unistd.h>
  23. #endif
  24. #ifndef S_ISREG
  25. #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
  26. #endif
  27. #ifndef S_ISDIR
  28. #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
  29. #endif
  30. #ifdef aFunWIN32_NO_CYGWIN
  31. typedef struct _stat64 aFun_stat;
  32. typedef wchar_t aFun_path;
  33. #else
  34. typedef struct stat aFun_stat;
  35. typedef char aFun_path;
  36. #endif
  37. /**
  38. * 获取文件的stat结构体
  39. * @param stat stat 保存地址
  40. * @param path 路径 (utf-8)
  41. * @return
  42. */
  43. static int get_stat(aFun_stat &stat_, const std::string &path_){
  44. int re;
  45. #ifdef aFunWIN32_NO_CYGWIN
  46. aFun_path *tmp = nullptr;
  47. if (convertWideByte(&tmp, path_.c_str(), CP_UTF8) == 0)
  48. return -1;
  49. re = _wstat64(tmp, &stat_);
  50. free(tmp); // 如果 path 为nullptr, 则释放最新生成的 wchat_t
  51. #else
  52. re = stat(path_.c_str(), &stat_);
  53. #endif
  54. return re;
  55. }
  56. /**
  57. * 目标判断文件类型, 若是普通文件返回1, 若是文件夹返回2, 其他遇到错误返回0
  58. */
  59. int aFuntool::checkFile(const std::string &path){
  60. if (path.empty())
  61. return 0;
  62. int re = 0;
  63. aFun_stat stat;
  64. if (get_stat(stat, path) != 0)
  65. re = 0;
  66. else if (S_ISREG(stat.st_mode)) // 普通文件
  67. re = 1;
  68. else if (S_ISDIR(stat.st_mode))
  69. re = 2;
  70. return re;
  71. }
  72. /**
  73. * 获取文件最后修改时间
  74. */
  75. time_t aFuntool::getFileMTime(const std::string &path) {
  76. aFun_stat stat;
  77. if (path.empty() || get_stat(stat, path) != 0)
  78. return 0;
  79. return stat.st_mtime;
  80. }
  81. /**
  82. * 拼接路径
  83. * @param path 路径
  84. * @param name 文件名
  85. * @param suffix 后缀
  86. * @return
  87. */
  88. std::string aFuntool::joinPath(const std::string &path, const std::string &name, const std::string &suffix) {
  89. std::string name_suffix = name + suffix;
  90. if (!path.empty() && *(path.end()) == SEP_CH)
  91. return path + name_suffix;
  92. else if (!path.empty())
  93. return path + SEP + name_suffix;
  94. return name_suffix;
  95. }
  96. /**
  97. * 给定路径获取该路径所指定的文件名
  98. */
  99. std::string aFuntool::getFileName(const std::string &path){
  100. int sep = 0;
  101. if (*(path.end()) == SEP_CH) // 若路径的最后一个字符为SEP, 则忽略此SEP
  102. sep = -1;
  103. auto slash = path.find_last_of('/');
  104. if (slash == std::string::npos)
  105. slash = 0;
  106. else
  107. slash++;
  108. return path.substr(path.size() - slash + sep, slash);
  109. }
  110. /**
  111. * 获取 文件路径+文件名(排除后缀)
  112. */
  113. std::string aFuntool::getFilePathName(const std::string &path){
  114. auto point = path.find_last_of('.');
  115. if (point == std::string::npos)
  116. return path;
  117. return path.substr(point);
  118. }
  119. /**
  120. * 获取文件路径(不包含文件名)
  121. */
  122. std::string aFuntool::getFilePath(const std::string &path, int dep){
  123. std::string::size_type point = path.size();
  124. for (int i = 0; i < dep; i++) {
  125. auto tmp = path.rfind(SEP_CH, point - 1);
  126. if (tmp == std::string::npos)
  127. break;
  128. point = tmp;
  129. }
  130. return path.substr(0, point);
  131. }
  132. /**
  133. * 获取文件后缀
  134. */
  135. std::string aFuntool::getFileSurfix(const std::string &path) {
  136. auto point = path.find_last_of('.');
  137. if (point == std::string::npos)
  138. point = 0;
  139. else
  140. point++;
  141. std::string ret = path.substr(path.size() - point, point);
  142. return ret;
  143. }
  144. /**
  145. * 把一个文件名转换为合法的变量名(替换不合法字符为_)
  146. * @param name 路径
  147. * @param need_free 是否需要释放
  148. */
  149. std::string aFuntool::fileNameToVar(const std::string &name){
  150. char *var = strCopy(name.c_str()); // 复制新的数据再修改
  151. if (!isalpha(*var) && *var != '_')
  152. var = strJoin("_", var, false, true);
  153. for (char *tmp = var; *tmp != 0; tmp++)
  154. if (!isalnum(*tmp) &&'_' != *tmp)
  155. *tmp = '_';
  156. std::string ret = var;
  157. free(var);
  158. return ret;
  159. }
  160. /**
  161. * 转换路径为合法路径(相对路径->绝对路径, 绝对路径保持不变)
  162. * @param path 文件路径
  163. * @param env 环境 必须以 / 结尾
  164. * @param need_free 是否需要释放 path
  165. */
  166. std::string aFuntool::findPath(const std::string &path, const std::string &env){
  167. #ifdef __linux
  168. if (path[0] != SEP_CH) // 不以 / 开头
  169. #else
  170. if (!(isupper(path[0]) && (path)[1] == ':')) // 不以盘符开头
  171. #endif
  172. return env + path; // 调整为相对路径模式
  173. return path;
  174. }
  175. /**
  176. * 获取可执行程序目录
  177. * @param dep 从可执行程序往回跳出的层数
  178. */
  179. std::string aFuntool::getExedir(int dep) {
  180. aFun_path exepath[218] = {0};
  181. #ifdef aFunWIN32_NO_CYGWIN
  182. DWORD ret = GetModuleFileNameW(nullptr, exepath, 217); // 预留一位给NUL
  183. if (ret == 0 || wcslen(exepath) == 0)
  184. return "";
  185. char *path = nullptr;
  186. if (convertFromWideByte(&path, exepath, CP_UTF8) == 0)
  187. return "";
  188. std::string re = getFilePath(path, dep + 1);
  189. free(path);
  190. return re;
  191. #else
  192. ssize_t ret = readlink("/proc/self/exe", exepath, 217); // 预留一位给NUL
  193. if (ret == -1 || strlen(exepath) == 0)
  194. return "";
  195. return getFilePath(exepath, dep + 1);
  196. #endif
  197. }
  198. /**
  199. * @param path 文件路径 (utf-8)
  200. * @return 文件大小
  201. */
  202. uintmax_t aFuntool::getFileSize(const std::string &path) {
  203. aFun_stat stat;
  204. int ret;
  205. ret = get_stat(stat, path);
  206. if(ret != 0)
  207. return 0; // 获取失败。
  208. return (uintmax_t)stat.st_size; // 返回文件大小
  209. }
  210. /**
  211. * 检查给定字符串是否utf-8编码
  212. * @param str 字符串
  213. */
  214. bool aFuntool::isCharUTF8(const char *str) {
  215. int code = 0; // utf-8 多字节数
  216. for (const char *ch = str; *ch != NUL; ch++) {
  217. unsigned char c = *ch;
  218. unsigned char c_ = ~c;
  219. if (SysLogger)
  220. assertFatalErrorLog(code >= 0 && code <= 5, SysLogger, 2, "str = %s", str);
  221. if (code == 0) {
  222. if ((c_ & 0xFC) == 0 && (c & 0x02) == 0) // 检查是否为1111110x, 先对其取反, 使用0xFC掩码检查前6位是否为0, 然后单独检查倒数第二位是否为0
  223. code = 5; // 剩余 5 个字节
  224. else if ((c_ & 0xF8) == 0 && (c & 0x04) == 0)
  225. code = 4; // 剩余 4 个字节
  226. else if ((c_ & 0xF0) == 0 && (c & 0x08) == 0)
  227. code = 3; // 剩余 3 个字节
  228. else if ((c_ & 0xE0) == 0 && (c & 0x10) == 0)
  229. code = 2; // 剩余 2 个字节
  230. else if ((c_ & 0xC0) == 0 && (c & 0x20) == 0)
  231. code = 1; // 剩余 1 个字节
  232. else if ((c & 0x80) == 0) // 检查最高位是否为0
  233. code = 0;
  234. else
  235. return false;
  236. } else if ((c_ & 0x80) == 0 && (c & 0x40) == 0)
  237. code--;
  238. else
  239. return false;
  240. }
  241. return true;
  242. }
  243. bool aFuntool::isCharUTF8(const std::string &str) {
  244. return isCharUTF8(str.c_str());
  245. }
  246. /**
  247. * 打开指定文件
  248. * @param path_ 路径 (utf-8)
  249. * @param mode_ 模式
  250. * @return
  251. */
  252. FILE *aFuntool::fileOpen(const char *path_, const char *mode_) {
  253. if (strlen(mode_) >= 5)
  254. return nullptr;
  255. #ifdef aFunWIN32_NO_CYGWIN
  256. FILE *file = nullptr;
  257. wchar_t *path = nullptr;
  258. wchar_t mode[5];
  259. if (convertWideByte(&path, path_, CP_UTF8) == 0)
  260. return nullptr;
  261. for (int i = 0; i < 5; i++)
  262. mode[i] = (wchar_t)mode_[i]; // ascii字符转换
  263. _wfopen_s(&file, path, mode);
  264. free(path);
  265. return file;
  266. #else
  267. return fopen(path_, mode_);
  268. #endif
  269. }
  270. FILE *aFuntool::fileOpen(const std::string &path_, const char *mode_) {
  271. return fileOpen(path_.c_str(), mode_);
  272. }
  273. /**
  274. * 关闭文件, 本质和fclose一样
  275. * @param file FILE
  276. * @return
  277. */
  278. int aFuntool::fileClose(FILE *file) {
  279. return fclose(file);
  280. }