2009年5月20日星期三

计划-20090520

虽然最近工作比较忙,但是还是想把算法再捡一捡。
1.算法
USACO 做题(每周至少三道)
2.编译器的学习
继续读spidermonkey的代码
继续读Engineering a Compiler
外加英语听力,感觉好忙呀。

2009年3月9日星期一

使用os.walk要当心

今天用python写了一段脚本,作用是遍历目录查找里面文件的内容。
遍历目录的代码使用了os.walk,大致如下:

def all_files(path):
for root, dirs, files in os.walk(path, topdown=False):
for name in files:
yield os.path.join(root, name)

调用的时候传入一个路径的参数,根据路径返回目录下的文件。

print list(all_file("c:/test"))

但是实际执行的时候,test目录下总是有些文件取不到。
改成u"c:/test"就都能取到了。
查看了一下os.walk的代码,os.walk做的就是调用listdir,在对子目录进行递归调用。


try:
# Note that listdir and error are globals in this module due
# to earlier import-*.
names = listdir(top)
except error, err:
if onerror is not None:
onerror(err)
return

dirs, nondirs = [], []
for name in names:
if isdir(join(top, name)):
dirs.append(name)
else:
nondirs.append(name)

if topdown:
yield top, dirs, nondirs
for name in dirs:
path = join(top, name)
if followlinks or not islink(path):
for x in walk(path, topdown, onerror, followlinks):
yield x
if not topdown:
yield top, dirs, nondirs

经过调试,问题这里的listdir上。
再查看到listdir的代码(posixmodule.c)

static PyObject *
posix_listdir(PyObject *self, PyObject *args)
{
/* XXX Should redo this putting the (now four) versions of opendir
in separate files instead of having them all here... */
#if defined(MS_WINDOWS) && !defined(HAVE_OPENDIR)

PyObject *d, *v;
HANDLE hFindFile;
BOOL result;
WIN32_FIND_DATA FileData;
char namebuf[MAX_PATH+5]; /* Overallocate for \\*.*\0 */
char *bufptr = namebuf;
Py_ssize_t len = sizeof(namebuf)-5; /* only claim to have space for MAX_PATH */

#ifdef Py_WIN_WIDE_FILENAMES
/* If on wide-character-capable OS see if argument
is Unicode and if so use wide API. */
if (unicode_file_names()) {
PyObject *po;
if (PyArg_ParseTuple(args, "U:listdir", &po)) {
......
}
#endif

if (!PyArg_ParseTuple(args, "et#:listdir",
Py_FileSystemDefaultEncoding, &bufptr, &len))
return NULL;
if (len > 0) {
char ch = namebuf[len-1];
if (ch != SEP && ch != ALTSEP && ch != ':')
namebuf[len++] = '/';
}
strcpy(namebuf + len, "*.*");

if ((d = PyList_New(0)) == NULL)
return NULL;

hFindFile = FindFirstFile(namebuf, &FileData);
......
#endif /* which OS */
} /* end of posix_listdir */

上面代码是我删减过的,去掉了根据不同操作系统的预处理,只留下windows相关的。
这里根据传递的路径是字符串还是unicode,执行不同的分支。
如果传递的是字符串,则从26行开始执行。
首先对传入的路径进行处理。
如果路径不是以\\结尾的,则追加上/,然后再追加上*.*
所以如果传递的是c:/test,处理之后就变成了c:/test/*.*。
看起来没有毛病,但是对于有些字符集(例如shift_jis),处理\\以外,有些字符也是以\\结尾的。
比如说在shift_jis
>>> '表'
'\x95\\'
所以如果以'表'结尾的目录经过处理不会变成
'表/*.*' 而是'表*.*'
这时取到的不是这个目录里面的内容,而是目录本身!!!
listdir正常返回,只是取到结果不对。
这个结果返回给os.walk,所以得到的结果也不对。

结论:
使用os.walk时,一定要用unicode。