2

我有大约 20 MB 大小且具有随机文件名的二进制数据文件,均以“AA”开头。在每个文件的内容中,它们在固定位置都有一个特定的字符串(在所有文件中从第 2086 个字节开始)。我想读取由 2 个单词组成的字符串(在下面的示例中,中间有 1 个空格,如“MyName Sirname”)并将其与文件创建日期一起使用来重命名文件。

这是一个缩短的示例文件(前 3Kb): dl.dropboxusercontent.com/u/18286876/short.zhr

我们想将此特定文件重命名为“MyName Sirname YYYY-MM-DD”。

如果脚本遍历 currant 目录中以“AA”开头的所有文件,那将是最好的。该脚本可以是 vbs 或批处理 + vbs 组合,无论哪种更简单。

这可能看起来是重复的,但最初的问题缺乏细节,错误地集中在批处理上并且给定的答案是不够的。

4

2 回答 2

1

假设您的所有文件都在同一个文件夹中,这样的事情可能会起作用C:\some\where

Const offset = 2085

Set fso = CreateObject("Scripting.FileSystemObject")

For Each f In fso.GetFolder("C:\some\where").Files
  If Left(f.Name, 2) = "AA" Then
    Set stream = f.OpenAsTextStream
    stream.Skip(offset)

    words = Array()
    Do
      length = Asc(stream.Read(1))
      If length <> 0 Then
        ReDim Preserve words(UBound(words)+1)
        words(UBound(words)) = stream.Read(length)
      End If
    Loop Until length = 0 Or stream.AtEndOfStream

    stream.Close

    If UBound(words) >= 1 Then
      fdate = Year(f.DateCreated) & "-" & Right("0" & Month(f.DateCreated), 2) _
        & "-" & Right("0" & Day(f.DateCreated), 2)
      f.Name = words(0) & " " & words(1) & " " & fdate _
        & "." & fso.GetExtensionName(f.Name)
    End If
  End If
Next
于 2013-06-11T13:19:30.860 回答
1

注意该文件似乎是“复合文档文件 V2 文档”格式。可能有一些图书馆可以以适当的方式阅读它。

疯狂猜测:您是否正在尝试“阅读” Outlook.msg文件、word/excel 文档?

使用file或查看


添加了更新C++ 版本(见下文

对该文件稍作修改告诉我这是一个二进制文件,字符串没有分隔,但前面是它们的长度字节。所以,这个 bash 脚本应该可以正常工作:

#!/bin/bash
set -e # stop on errors

for originalname in "$@"
do
    # get lengths
    first_len=$(od -j 2085 "$originalname" -An -t u1 -N1)
    second_len=$(od -j $((2086 + $first_len)) "$originalname" -An -t u1 -N1)

    # strip whitespace
    read first_len second_len <<< "$first_len $second_len"

    # extract the words as text
    firstword=$(dd if="$originalname" bs=1 skip=2086 count=$first_len)
    secondword=$(dd if="$originalname" bs=1 skip=$((2087+$first_len)) count=$second_len)

    # calculate new name, using the timestamp of the file too:
    newname="$firstword $secondword $(date -r "$originalname" +"%Y-%m-%d")"

    # do the move (verbosely)
    mv -v "$originalname" "$(dirname "$originalname")/$newname"
done

我在您提供的文件上对其进行了测试:

$ ./test.sh short.zhr 2>/dev/null

   `short.zhr' -> `./MyName Sirname 2013-06-11'

你一定会喜欢 UNIX 哲学 :)

对于您的情况,您可以运行

 ./test.sh somedir/AA*

C++ 版本

为了好玩,我写了一个 C++ 版本。这应该很容易移植。

它实际上更具可读性(除了格式化时间戳的部分......)。

#include <string>
#include <vector>
#include <fstream>
#include <ctime>
#include <cstdlib>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include <iostream>

std::string extract_string(std::istream& is) {
    char len;
    if (is && is.read(&len, 1)) {
        std::string result(len, '\0');
        is.read(&*result.begin(), len);
        return result;
    }
    return "";
}

std::string timestamp(std::string const& fname, const char* fmt = "%Y-%m-%d")
{
    struct stat sb;
    if (-1 == stat(fname.c_str(), &sb))
        perror("cannot get file stats");

    if (struct tm* tmp = localtime(&sb.st_ctime))
    {
        std::string buf(200, '\0');
        buf.resize(strftime(&*buf.begin(), buf.size(), fmt, tmp));
        return buf;
    } else
        perror("localtime failed");
    return "";
}

int main(int argc, const char *argv[])
{
    for (int i = 1; i<argc; ++i)
    {
        const std::string fname(argv[i]);
        std::ifstream stream(fname.c_str(), std::ios::binary);

        stream.seekg(2085);
        std::string first  = extract_string(stream);
        std::string second = extract_string(stream);

        std::string newname = first + " " + second + " " + timestamp(fname);
        std::cout << (("rename \"" + fname + "\" \""  + newname + "\"").c_str());
    }
}

你会以完全相同的方式使用它。当然,您可以改为打印newname并从您自己的脚本中使用它。 编辑编辑版本以交叉编译为 win-exe。让它打印一个rename命令。

于 2013-06-11T10:57:13.753 回答