文件夾復(fù)制的命名算法(仿mac)

需求:一個文件夾被復(fù)制,要求新文件夾的名字合適且不重復(fù)。

在 nodejs后端吻商,我在實現(xiàn)文件夾復(fù)制的功能時利职,發(fā)現(xiàn)簡單的給原名子添加 “的副本” 作為新名字趣效,復(fù)制多次的話得到的“...的副本的副本...”特別長,且沒有什么意義猪贪,于是決定模仿 mac 的文件復(fù)制的命名的表現(xiàn)跷敬,寫了個命名算法。 這個算法的代碼實現(xiàn)很簡單热押,難的是理解需求和詳細規(guī)則(即為什么要這么做)西傀。

關(guān)于mac文件重命名的規(guī)則

  • 每次復(fù)制文件斤寇,如果不是“的副本”結(jié)尾的文件,復(fù)制的新文件會加上“的副本( n)”拥褂,這里的n是自然數(shù)字娘锁,不含前導(dǎo)零。括號是指可能有饺鹃,也可能沒有空格和數(shù)字
  • 如果原文件名是“的副本”結(jié)尾莫秆,復(fù)制出的新文件的名字會加上“ n”。
  • 如果原文件名是“的副本 n”尤慰,復(fù)制出的新文件名結(jié)尾的數(shù)字會比原名的索引大且向上最接近原名且不和其他文件重名馏锡。
  • 復(fù)制時,如果原文件夾是“的副本”加上有前導(dǎo)0時伟端,會去掉前導(dǎo)0杯道,并應(yīng)用上一條規(guī)則。
  • 文件夾視為文件责蝠,即創(chuàng)建的新文件名不能和當前文件夾下的文件重名

只要明確了重命名的詳細規(guī)則党巾,我們就很容易明確如何算法的實現(xiàn)細節(jié)。

2019.3.25更新

優(yōu)化 重寫了代碼霜医,包裝成一個方法齿拂,可以自定義后綴名。

/**
 * 文件復(fù)制的命名算法
 * @param {string} oName 被復(fù)制的文件的名稱
 * @param {Array} filenames 目錄下的所有文件名數(shù)組
 * @param {string} suffix 后綴(默認為'的副本')
 */
const getCopyedName = (oName, filenames, suffix = '的副本') => {
    let index;
    let root;   // 詞根
    let match = new RegExp(`${suffix}( \\d+)?$`).exec(oName);
    console.log(match);

    // 1. 求出 oName 的 索引值 和 詞根(不含后綴和索引的源文件名 )
    if (match) {
        if (match[1]) index = parseInt(match[1]);
        else index = 0;
        
        root = oName.slice(0, match.index);
    } else {
        index = 0;
        root = oName;
    }

    console.log('被復(fù)制文件的詞根:', root);
    console.log('被復(fù)制文件的索引:', index);

    // 2. 根據(jù)“詞根”肴敛,獲得當前目錄下的相同詞根 索引列表署海。純詞根(沒有后綴)不在范圍內(nèi),因為復(fù)制的新名字必然有后綴医男。
    const reg = new RegExp(`^${root}${suffix}( [1-9][0-9]*)?$`);   // 注意這里要求為非0開始的數(shù)字
    let indexs = [];
    filenames.forEach((item) => {
        match = reg.exec(item);
        if (match) {
            const i = match[1] ? parseInt(match[1]) : 1;
            indexs.push(i);
        }
    })
    // indexs 理論上不會有重復(fù)的值砸狞。如果你的目錄可能會有重名的文件,請做“去重”處理镀梭。

    // 3. 尋找可用的 index
    // 從 indexs 找出與 index + 1 相等的值刀森,如果不存在,新的文件名即為 root + suffix + (index+1)
    // 如果存在报账,繼續(xù)找出 index + 2 的值研底,直到發(fā)現(xiàn)一個 index + n 在 數(shù)組中不存在。
    indexs.sort((a,b)=> a - b);
    console.log('目錄下的同詞根的文件index(排序):', indexs);
    index++; 
    for (let i = 0, len = indexs.length; i < len; i++) {
        if (indexs[i] == index) index++;
        else if (indexs[i] > index) break;
    }

    console.log('復(fù)制后的文件名:', root + suffix + ' ' + index);

    return root + suffix + ' ' + index;
}

// 測試
const filenames = [
    'aa的副本 23', 'aa的副本 003', 'bb', 'aa', 'aa的副本', '的副本', 'aa的副本 12',
    'aa的副本 13', 'aa的副本 14'
];
getCopyedName('aa的副本 12', filenames);

下面的內(nèi)容是很久以前寫的透罢,看思路就行了榜晦,舊版的代碼(不優(yōu)雅)不要太過在意。

實現(xiàn)

下面是具體的實現(xiàn)琐凭,不過是針對文件夾的芽隆,其實換成文件也完全沒問題。

確定原文件夾的索引

首先獲取原文件夾名字统屈,然后定義一個 index胚吁,用于確定原文件“xx的副本 n”的 n 的取值。

let regExpName = oFolder.name;  
let index;

接著我們確定原文件夾名稱的 n 到底是哪個愁憔,n 的獲取方式如下表腕扶。

原文件夾名 n
xx 0
xx的副本 1
xx的副本 2 2
xx的副本 002 2
... ...

注:不會復(fù)制出名為“xx的副本 1”的文件夾。

為了讀取名字結(jié)尾的數(shù)字吨掌,使用了正則表達式半抱。

if (/的副本( \d+)?$/g.test(oFolder.name)) {
    let r = / \d+$/g.exec(oFolder.name);
    if (r == null) {
        // 文件夾名格式: xx的副本
        index = 1;   
    } else {
        // 格式:xx的副本 n (n可以包含前導(dǎo)0)
        index = parseInt(r[0]);   
        regExpName = oFolder.name.slice(0, r.index);
    }
} else {
    //  格式:xx
    regExpName = regExpName; 
    index = 0;      
}

通過正則表達式,已經(jīng)知道了原文件夾的名字符合的是哪一種情況膜宋,并給 index 賦予了正確的值窿侈。這里還對 regExpName 進行操作,使其為“的副本”結(jié)尾秋茫,以方便接下來的查詢其他文件夾的操作作為正則表達式的一部分史简。

得到符合“xx的副本 n”的所有文件夾名字

接下來是找出原文件夾所在文件夾系的所有文件夾中,符合 /^${regExpName}的副本( [1-9][0-9]*)?$/ 的所有名字肛著,正則表達式的意思是要求符合“xx的副本”或者“xx的副本 n”(注意這個 n 是不含前導(dǎo)0的數(shù)字)圆兵。

let checkedNameFolders = await models.Folder.findAll({
    where: {
        parentId: oFolder.parentId,
        name: {
            $regexp: `^${regExpName}的副本( [1-9][0-9]*)?$`
        }
    }
});

let checkedName = checkedNameFolders.map(item => {
    let name = item.name;
    let e = /\d+$/g.exec( name );
    if(e == null) return 1;
    return parseInt( e[0] );
});

// 去重并排序
checkedName = [...new Set(checkedName)].sort((a, b) => a - b);

這里我是用了 sequelize 獲取了 數(shù)據(jù)庫中所有名字符合該正則表達式的文件夾對象,取得了文件夾對象的 name枢贿,并判斷 name 的結(jié)尾是否有數(shù)字殉农,有數(shù)字的話,就提取這個成數(shù)字局荚,放到 checkedName 數(shù)組內(nèi)超凳,沒有的話就返回1。最后我們對這個數(shù)組進行去重和升序耀态,理論上去重操作是不需要的轮傍,但誰知道數(shù)據(jù)庫里面會發(fā)生什么事情呢。不管怎樣茫陆,穩(wěn)妥起見做個去重金麸。

循環(huán)找出可以使用的索引

let newIndex = 1;
for (let i = index + 1; ;i++) {
    if (!checkedName.includes(i)) {
        newIndex = i;
        break;
    }
}

我們會從原文件夾的索引+1后,進行遞增并判斷該數(shù)組里時候含有這個值簿盅,一旦發(fā)現(xiàn)沒有挥下,就確定了我們的復(fù)制文件夾索引,停止循環(huán)桨醋。(理論上這里的算法是可以優(yōu)化的棚瘟,因為我們之前已經(jīng)給數(shù)組排序了,而inclues方法每次都要遍歷數(shù)組效率并不高喜最,在文件數(shù)量很多的情況下可能不好使了偎蘸。)

根據(jù)確定后的可用索引映射回文件名

根據(jù)我之前給出的表格,從索引得出最終的名字。

let newName;
switch (newIndex) {
    case 1:
        newName = `${regExpName}的副本`;
        break;

    default:
        newName = `${regExpName}的副本 ${newIndex}`;
        break;
}

到了這里迷雪,我們就獲得了想要的新文件夾的名字了限书。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市章咧,隨后出現(xiàn)的幾起案子倦西,更是在濱河造成了極大的恐慌,老刑警劉巖赁严,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扰柠,死亡現(xiàn)場離奇詭異,居然都是意外死亡疼约,警方通過查閱死者的電腦和手機卤档,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來程剥,“玉大人劝枣,你說我怎么就攤上這事〕” “怎么了哨免?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長昙沦。 經(jīng)常有香客問我琢唾,道長,這世上最難降的妖魔是什么盾饮? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任采桃,我火速辦了婚禮,結(jié)果婚禮上丘损,老公的妹妹穿的比我還像新娘普办。我一直安慰自己,他們只是感情好徘钥,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布衔蹲。 她就那樣靜靜地躺著,像睡著了一般呈础。 火紅的嫁衣襯著肌膚如雪舆驶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天而钞,我揣著相機與錄音沙廉,去河邊找鬼。 笑死臼节,一個胖子當著我的面吹牛撬陵,可吹牛的內(nèi)容都是我干的珊皿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼巨税,長吁一口氣:“原來是場噩夢啊……” “哼蟋定!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起垢夹,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤溢吻,失蹤者是張志新(化名)和其女友劉穎维费,沒想到半個月后果元,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡犀盟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年而晒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阅畴。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡倡怎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出贱枣,到底是詐尸還是另有隱情监署,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布纽哥,位于F島的核電站钠乏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏春塌。R本人自食惡果不足惜晓避,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望只壳。 院中可真熱鬧俏拱,春花似錦、人聲如沸吼句。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惕艳。三九已至搞隐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尔艇,已是汗流浹背尔许。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留终娃,地道東北人味廊。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親余佛。 傳聞我的和親對象是個殘疾皇子柠新,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,928評論 2 89
  • 一、Java 簡介 Java是由Sun Microsystems公司于1995年5月推出的Java面向?qū)ο蟪绦蛟O(shè)計...
    子非魚_t_閱讀 4,183評論 1 44
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理辉巡,服務(wù)發(fā)現(xiàn)恨憎,斷路器,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 【日文】人學(xué)ばざれば道を知らぬ (ひとまなばざればみちをしらぬ) 【中文】人不學(xué)郊楣,不知道 【感悟】不...
    行禪者閱讀 314評論 0 0
  • 【柳梢青·上恒究遥】(新韻) 座落江東。中西交匯净蚤,今古通融钥组。不夜申城,明珠璀璨今瀑,指向蒼穹程梦。 恰明月共潮生,看此地橘荠,騰飛...
    冷楓葉閱讀 515評論 2 4