当前位置: 代码迷 >> Android >> Android文件重命名File.renameTo()以及定义副本名步骤(自定义规则)
  详细解决方案

Android文件重命名File.renameTo()以及定义副本名步骤(自定义规则)

热度:510   发布时间:2016-04-28 00:11:58.0
Android文件重命名File.renameTo()以及定义副本名方法(自定义规则)

项目需求

做文件管理相关项目有个需求需要对单个或多个文件进行重命名,这就可能会出现名称重复的情况;还有复制的时候,如果粘贴的地方已存在相同名称文件,也需要进行重命名。

相仿思想:

我们知道在电脑上复制粘贴同一文件(夹)到同一路径下的时候,系统会帮我们自动生成新的副本(Copy)名

比如: 我.png
MAC是文件名 + ” ” + 数字递增 + 后缀 (我 1.png)
Windows是文件名 + ” - 副本”+ ” (” + 数字递增 + “)” + 后缀 (我 -副本(2).png)

而在电脑上重命名时,如果该路径下已有该名称文件,则会提示(警告)用户并不允许进行重命名。

知识点

本节将讲解的内容有:
1.如何调用File固有方法去进行本地文件重命名
2.需要新建副本名时,该怎么用自定义规则进行重命名

代码分析

根据需求,我们可以定义接口重命名
public boolean rename(FileInfo fileInfo, String newName);

其中FileInfo是自定义数据结构,里面包含fileName(文件名),filePath(文件路径),isDir(是否是目录)等信息。
参数newName是新的文件名(包含后缀)

    /**      *  @Description 对文件进行重新命名      *  @param fileInfo 原始文件信息      *  @param newName 新名称(有后缀)      *  @param admitCopyName 当命名冲突时,是否允许生成副本名(如果是多选重命名的话,是需要允许的;单个文件重命名则设置为不允许)      *  @return 是否修改成功      */    public boolean rename(FileInfo fileInfo, String newName, boolean admitCopyName) {        //1.判断参数阈值        if (fileInfo == null || newName == null) {            Log.e(LOG_TAG, "Rename: null parameter");            return false;        }        //2.得到原文件全路径        String oldPath = fileInfo.getFilePath();        Log.d(LOG_TAG, "Rename---original path = " + oldPath));        //3-1.得到文件所在路径        String rootPath = Util.getPathFromFilepath(oldPath); //Util.getPathFromFilepath(String)-自定义方法:得到文件所在路径(即全路径去掉完整文件名)        //3-2.得到新全路径        String newPath = Util.makePath(rootPath, newName); //Util.makePath(String, String)-自定义方法:根据根路径和文件名形成新的完整路径        Log.d(LOG_TAG, "Rename---new Path = " + newPath);        //4.比较是否变更了名称        if (oldPath.endsWith(newPath)) { //和原来名称一样,不需要改变            return true;        }        try {            //5.根据新路径得到File类型数据            File newFile = new File(newPath);            //6.判断是否已经存在同样名称的文件(即出现重名)            if (newFile.exists() && !admitCopyName) { //出现重命名且不允许生成副本名                return false; //重命名失败            }            //7.循环判断直到生成可用的副本名            while (newFile.exists()) {                Log.w(LOG_TAG, "Rename---新文件路径名称已存在文件 ---" + newPath);                //重命名重名定义规范--Util.getCopyNameFromOriginal(String)-自定义方法:根据自定义规则生成原名称副本                newPath = Util.getCopyNameFromOriginal(newPath);                newFile = new File(newPath);                Log.i(LOG_TAG, "Rename---new copy Path = " + newPath);            }            //8.得到原文件File类型数据            File file = new File(oldPath);            //9.调用固有方法去重命名            boolean ret = file.renameTo(newFile);            Log.i(LOG_TAG, "Rename---改名成功? " + ((ret) ? "yes!" : "no!"));            if (ret) {                //FIXME:这里通过更改形参来改变实参,是不好的写法,不建议这样写!                fileInfo.setFileName(Util.getNameFromFilepath(newPath)); //更新文件名                fileInfo.setmFilePath(newPath); //更新新路径             }            return ret;        } catch (SecurityException e) {            Log.e(LOG_TAG, "Fail to rename file," + e.toString());        }        return false;    }

分步讲解

中心内容

代码量看起来有点长,其实中心就一句,就是第9步中的file.renameTo(newFile);

这是调用File的固有重命名方法,其用法如下

//oldPath like "mnt/sda/sda1/我.png"File file = new File(oldPath); //newPath like "mnt/sda/sda1/我的照片.png"file.renameTo(new File(newPath));

需要注意的是:

第一,如果是Android的SD卡上的文件重命名,那么必须添加权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

第二,oldPath和newPath必须是新旧文件的绝对路径

第三,如果newPath已经存在文件,不做任何处理的话,调用此方法会把已存在的文件覆盖掉。

意思是:比如我在”mnt/sda/sda1/”路径下有两张照片,一个叫”我.png”,一个叫”我的照片.png”,这个时候我用file.renameTo(new File(newPath));把”我.png”重命名为”我的照片.png”,那么原来存在的”我的照片.png”会被重命名后的”我.png”覆盖。

:第三点确实是我实践后总结出来的,但是没有进行过大量的验证,若有不对的地方,还望指教。

第四,在网上看到一些人为了测试renameTo的用法,直接在没有实体文件的情况下,就创建路径,然后进行重命名。

File t1 = new File("D:" + File.separatorChar + "final.java");File t2 = new File("D:" + File.separatorChar + "finalaa.java");System.out.println("是否重命名成功:" + t1.renameTo(t2)); //输出false

需注意的是,new File(filePath)得到的是一个路径,并不是一个实体文件!当你文件都没有创建的时候,怎么可能对一个不存在的文件进行重命名呢?

如下解释我觉得更好理解

我的理解是new File(String path)并没有创建文件本身,只是说我可能要在这个路径创建文件。

t1被创建以后(调用了createNewFile),文件就实实在在地在那里了,重命名为finalaaa.java以后,文件还是t1这个文件,只是路径改变了,从而使名字变了。

由始至终只有一个文件(也就是说t2只是把地方标出来了,并没有得到创建,当t1想占用t2的地盘的时候,t2让给了t1)

辅助方法

可以看到重命名方法里调用了许多我自定义的辅助方法,
如3-1步

Util.getPathFromFilepath(oldPath); //得到文件所在路径(即全路径去掉完整文件名)

3-2步

Util.makePath(rootPath, newName); //根据根路径和文件名形成新的完整路径

第7步

Util.getCopyNameFromOriginal(newPath); //根据自定义规则生成原名称副本

这些方法的实现也不难,都放在Util工具包里是为了复用。

    /**     * @Description 得到文件所在路径(即全路径去掉完整文件名)     * @param filepath 文件全路径名称,like mnt/sda/XX.xx     * @return 根路径,like mnt/sda     */    public static String getPathFromFilepath(final String filepath) {        int pos = filepath.lastIndexOf('/');        if (pos != -1) {            return filepath.substring(0, pos);        }        return "";    }    /**     * @Description 重新整合路径,将路径一和路径二通过'/'连接起来得到新路径     * @param path1 路径一     * @param path2 路径二     * @return 新路径     */    public static String makePath(final String path1, final String path2) {        if (path1.endsWith(File.separator)) {            return path1 + path2;        }        return path1 + File.separator + path2;    }

上面两个确实比较简单,第三个方法getCopyNameFromOriginal(String)是取得文件的副本名称,里面定义了定义副本名称的规则(空格 + 数字递增),该规则是仿着Mac的重命名规则来定义的。

    /**     * @Description 得到文件副本名称,可供粘贴及多选重命名方法使用     * 命名规则为:普通文件后加“ 1”,若文件末尾已有“ 数字”,则数字递增。     * 比如,有个文件叫“我.jpg”,使用本方法后得到了“我 1.jpg”,再次使用本方法后得到“我 2.jpg”     * @param originalName 原本的名字,XXX.xx 或者完整路径 xx/xx/XXX.xx , 也可以没有后缀.xx     * @return 副本名称     */    public static String getCopyNameFromOriginal(final String originalName) {        //1.判断阈值        if (originalName == null || originalName.isEmpty()) {            return null;        }        String copyName = null;        //2.得到文件名和后缀名        String[] nameAndExt = getNameAndExtFromOriginal(originalName);        if (nameAndExt == null) {            return null;        }        String fileName = nameAndExt[0];        String fileExt = nameAndExt[1];        //3.判断文件名是否包含我们定义副本规范的标记字符(空格)        if (fileName.contains(" ")) { //如果文件名包涵空格,进行判断是否已经为副本名称            //4-1.得到end            String[] array = fileName.split(" ");            String end = array[array.length - 1]; //得到标记字符后面的值            //4-2.确保end得到的是最后面的值(防止出现类似路径中的目录也有标记字符的情况,如:"mnt/sda/wo de/zhao pian/我的 照片 1.png")            while(end.contains(" ")) {                array = fileName.split(" ");                end = array[array.length - 1];            }            //5.判断标记字符后的字符串是否复合规范(是否是数字)            boolean isDigit = end.matches("[0-9]+"); //用正则表达式判断是否是正整数                 if (isDigit) {                try {                    int index = Integer.parseInt(end) + 1; //递增副本记数                    int position = fileName.lastIndexOf(" "); //得到最后的空格的位置,用于截取前面的字符串                    if (position != -1) {                    //6-1.构造新的副本名(数字递增)                        copyName = fileName.substring(0, position + 1) + String.valueOf(index);                    }                } catch (Exception e) { //转化成整形错误                    e.printStackTrace();                    return null;                }            } else { //如果空格后不是纯数字,即不为我们定义副本的规范                //6-2.构造新的副本名(数字初始为1)                copyName = fileName + " 1";            }        } else { //如果没有,则变为副本名称格式            //6-3.构造新的副本名(数字初始为1)            copyName = fileName + " 1";        }        Log.d(TAG, "new copy name is " + copyName + fileExt);        //6.返回副本名+后缀名        return copyName + fileExt;    }

需要注意的有

第一点,可以把所有的标记字符(” “)提出来,用常量保存,COPY_NAME_TAG = " ";,这样我们想更改标记字符的时候(比如想改成”-“),只需要改常量就可以了。

第二点,我想讲的是第5步boolean isDigit = end.matches("[0-9]+"); //用正则表达式判断是否是正整数,关于如何判断一个字符串是否是数字。其实有很多种方法。

1.使用Character.isDigit(char)判断

思路:首先把String转换成char[],然后单个判断是否是数字,是就根据位数算出值,不是则输出错误的结果(-1),最后根据值进行判断使用。

char num[] = end.toCharArray();//把字符串转换为字符数组int result = 0;for (int i = 0; i < num.length; i++) {    if (Character.isDigit(num[i])) {        result += Integer.parseInt(Character.toString(num[i])) * Math.pow(10, length - 1); //当前数字乘以所在位数    } else { //一旦有一个不是字符,转换失败        result = -1;        break;    }}if (result > 0) {    //...}

第一种方法比较简单也比较麻烦,适合初学者使用。

2.使用类型强制转换

boolean isDigit;try {    int num=Integer.valueOf(end);//把字符串强制转换为数字    isDigit = true;//如果是数字,返回True} catch (Exception e) {    isDigit = false;//如果抛出异常,返回False}if (isDigit) {    //...}

这种用异常来判断是否是数字的方法比较怪异,我个人是不建议使用。

3.使用正则表达式判断

也就是我第5步boolean isDigit = end.matches("[0-9]+"); //用正则表达式判断是否是正整数

正则表达式[]表示可有可没有,0-9表示0-9之间(包含0和9)的的所有数字任选一个,+表示至少有一次,所以这样写就是表示正整数。

以下引用自网络,没有验证正确性。

用正则表达式,看看str.matches(regex)是不是返回true就行。其中regex的部分,对非负整型用”\d+”,有可选的负号的用”-?\d+”,可选正负号的用”(?:+|-)?\d+”,非负的浮点数用”\d+(?:\.\d+)?”,……


如果你有任何问题,请留言告诉我!

版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案