我正在创建一个包含子目录列表的文件
task createNotes {
doLast {
def myFile = new File("my-notes.txt")
def file = new File("src/test/")
println myFile.exists()
myFile.delete()
println myFile.exists()
println myFile.absolutePath
println file.absolutePath
myFile.withWriter {
out ->
file.eachDir { dir ->
out.println dir.getName()
}
}
}
}
显然无法保证排序顺序,但每次运行它时我都会得到相同的排序顺序:
soft
java
calc
conc
caab
pres
如果我将“soft”目录更改为“sofp”,则输出为:
java
sofp
calc
conc
caab
pres
当我改回名称时,它会恢复到原来的顺序。
它似乎没有按任何特定顺序排序 - 这与文档中所说的不能保证顺序相匹配,但如果是这样,为什么它每次总是给我相同的排序?
eachDir
Groovy 扩展方法的实现:
public static void eachDir(File self, @ClosureParams(value = SimpleType.class, options = "java.io.File") Closure closure) throws FileNotFoundException, IllegalArgumentException {
eachFile(self, FileType.DIRECTORIES, closure);
}
eachFile
有什么作用?
public static void eachFile(final File self, final FileType fileType, @ClosureParams(value = SimpleType.class, options = "java.io.File") final Closure closure)
throws FileNotFoundException, IllegalArgumentException {
checkDir(self);
final File[] files = self.listFiles();
// null check because of https://bugs.java.com/bugdatabase/view_bug?bug_id=4803836
if (files == null) return;
for (File file : files) {
if (fileType == FileType.ANY ||
(fileType != FileType.FILES && file.isDirectory()) ||
(fileType != FileType.DIRECTORIES && file.isFile())) {
closure.call(file);
}
}
}
File#listFiles
方法,然后迭代结果,而不会干扰现有的顺序。
Files#listFiles
通过 FileSystem#list
方法使用 normalizedList
。
FileSystem#list
是抽象的。继续两个最流行的实现,事实证明 UnixFileSystem#list
和 Win32FileSystem#list
都有一个 native
实现:
@Override
public native String[] list(File f);
深入研究 Windows 实现:
JNIEXPORT jobjectArray JNICALL
Java_java_io_WinNTFileSystem_list(JNIEnv *env, jobject this, jobject file)
{
WCHAR *search_path;
HANDLE handle;
WIN32_FIND_DATAW find_data;
int len, maxlen;
jobjectArray rv, old;
DWORD fattr;
jstring name;
jclass str_class;
WCHAR *pathbuf;
DWORD err;
str_class = JNU_ClassString(env);
CHECK_NULL_RETURN(str_class, NULL);
pathbuf = fileToNTPath(env, file, ids.path);
if (pathbuf == NULL)
return NULL;
search_path = (WCHAR*)malloc(2*wcslen(pathbuf) + 6);
if (search_path == 0) {
free (pathbuf);
errno = ENOMEM;
JNU_ThrowOutOfMemoryError(env, "native memory allocation failed");
return NULL;
}
wcscpy(search_path, pathbuf);
free(pathbuf);
fattr = GetFileAttributesW(search_path);
if (fattr == INVALID_FILE_ATTRIBUTES) {
free(search_path);
return NULL;
} else if ((fattr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
free(search_path);
return NULL;
}
/* Remove trailing space chars from directory name */
len = (int)wcslen(search_path);
while (search_path[len-1] == L' ') {
len--;
}
search_path[len] = 0;
/* Append "*", or possibly "\\*", to path */
if ((search_path[0] == L'\\' && search_path[1] == L'\0') ||
(search_path[1] == L':'
&& (search_path[2] == L'\0'
|| (search_path[2] == L'\\' && search_path[3] == L'\0')))) {
/* No '\\' needed for cases like "\" or "Z:" or "Z:\" */
wcscat(search_path, L"*");
} else {
wcscat(search_path, L"\\*");
}
/* Open handle to the first file */
handle = FindFirstFileW(search_path, &find_data);
free(search_path);
if (handle == INVALID_HANDLE_VALUE) {
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
// error
return NULL;
} else {
// No files found - return an empty array
rv = (*env)->NewObjectArray(env, 0, str_class, NULL);
return rv;
}
}
/* Allocate an initial String array */
len = 0;
maxlen = 16;
rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
if (rv == NULL) { // Couldn't allocate an array
FindClose(handle);
return NULL;
}
/* Scan the directory */
do {
if (!wcscmp(find_data.cFileName, L".")
|| !wcscmp(find_data.cFileName, L".."))
continue;
name = (*env)->NewString(env, find_data.cFileName,
(jsize)wcslen(find_data.cFileName));
if (name == NULL) {
FindClose(handle);
return NULL; // error
}
if (len == maxlen) {
old = rv;
rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
if (rv == NULL || JNU_CopyObjectArray(env, rv, old, len) < 0) {
FindClose(handle);
return NULL; // error
}
(*env)->DeleteLocalRef(env, old);
}
(*env)->SetObjectArrayElement(env, rv, len++, name);
(*env)->DeleteLocalRef(env, name);
} while (FindNextFileW(handle, &find_data));
err = GetLastError();
FindClose(handle);
if (err != ERROR_NO_MORE_FILES) {
return NULL; // error
}
if (len < maxlen) {
/* Copy the final results into an appropriately-sized array */
old = rv;
rv = (*env)->NewObjectArray(env, len, str_class, NULL);
if (rv == NULL)
return NULL; /* error */
if (JNU_CopyObjectArray(env, rv, old, len) < 0)
return NULL; /* error */
}
return rv;
}
我们可以看到
FindFirstFileW
、FindNextFileW
和 FindClose
WinAPI 函数的组合用于迭代文件。有关订购的摘录自 FindNextFileW
的文档:
不保证搜索返回文件的顺序(例如字母顺序),并且取决于文件系统。因此,在考虑到操作系统和文件系统类型限制的情况下,实现会以一种最佳方式列出文件。不保证特定订单。(...)
此函数返回文件名的顺序取决于文件系统类型。对于 NTFS 文件系统和 CDFS 文件系统,名称通常按字母顺序返回。对于 FAT 文件系统,名称通常按照文件写入磁盘的顺序返回,可能按字母顺序排列,也可能不按字母顺序排列。然而,如前所述,这些行为并不能得到保证。
*尼克斯
这是代码:
JNIEXPORT jobjectArray JNICALL
Java_java_io_UnixFileSystem_list(JNIEnv *env, jobject this,
jobject file)
{
DIR *dir = NULL;
struct dirent *ptr;
int len, maxlen;
jobjectArray rv, old;
jclass str_class;
str_class = JNU_ClassString(env);
CHECK_NULL_RETURN(str_class, NULL);
WITH_FIELD_PLATFORM_STRING(env, file, ids.path, path) {
dir = opendir(path);
} END_PLATFORM_STRING(env, path);
if (dir == NULL) return NULL;
/* Allocate an initial String array */
len = 0;
maxlen = 16;
rv = (*env)->NewObjectArray(env, maxlen, str_class, NULL);
if (rv == NULL) goto error;
/* Scan the directory */
while ((ptr = readdir(dir)) != NULL) {
jstring name;
if (!strcmp(ptr->d_name, ".") || !strcmp(ptr->d_name, ".."))
continue;
if (len == maxlen) {
old = rv;
rv = (*env)->NewObjectArray(env, maxlen <<= 1, str_class, NULL);
if (rv == NULL) goto error;
if (JNU_CopyObjectArray(env, rv, old, len) < 0) goto error;
(*env)->DeleteLocalRef(env, old);
}
#ifdef MACOSX
name = newStringPlatform(env, ptr->d_name);
#else
name = JNU_NewStringPlatform(env, ptr->d_name);
#endif
if (name == NULL) goto error;
(*env)->SetObjectArrayElement(env, rv, len++, name);
(*env)->DeleteLocalRef(env, name);
}
closedir(dir);
/* Copy the final results into an appropriately-sized array */
if (len < maxlen) {
old = rv;
rv = (*env)->NewObjectArray(env, len, str_class, NULL);
if (rv == NULL) {
return NULL;
}
if (JNU_CopyObjectArray(env, rv, old, len) < 0) {
return NULL;
}
}
return rv;
error:
closedir(dir);
return NULL;
}
本次迭代由opendir
/
readdir
/
closedir
三人组支持。
readdir
的POSIX文档仅提到了有关订购的内容:
头文件中定义的DIR类型表示目录流,它是特定目录中所有目录条目的有序序列。
Linux 文档 还有更多要说的:
连续调用 readdir() 读取文件名的顺序取决于文件系统的实现;名称不太可能以任何方式排序。距离 Windows 足够近,除了有
一些订单外,没有订单保证。
结论弃用(即使在上线后,通常也会给开发人员留出一些时间来调整他们的代码以适应新的代码)。 另一方面,“不受保证”功能的行为可能因给定产品/库版本、环境(例如 JVM 实现、操作系统、产品版本)甚至特定调用而异。它们有时会表现出一些可预测的特征,但不应依赖它们。您需要确保照顾好您期望的代码片段的保证并自行实现它们。 总结您的问题:如果您期望任何特定的文件顺序,只需先对它们进行排序 - 即使这意味着顺序将保持不变。