วิธีสร้างชื่อไฟล์เฉพาะใน C #


131

ฉันได้ใช้อัลกอริทึมที่จะสร้างชื่อเฉพาะสำหรับไฟล์ที่จะบันทึกในฮาร์ดไดรฟ์ ฉันกำลังต่อท้ายDateTime: ชั่วโมงนาทีวินาทีและมิลลิวินาทีแต่ก็ยังสร้างชื่อไฟล์ที่ซ้ำกันเนื่องจากฉันอัปโหลดไฟล์หลายไฟล์ในแต่ละครั้ง

อะไรคือทางออกที่ดีที่สุดในการสร้างชื่อเฉพาะสำหรับไฟล์ที่จะเก็บไว้ในฮาร์ดไดรฟ์ดังนั้นจึงไม่มี 2 ไฟล์ที่เหมือนกัน


ขึ้นอยู่กับข้อกำหนดอื่น ๆ คำถาม [เก่า] นี้ / คลุมเครือเกินไป
user2864740

คำตอบ:


240

ถ้าการอ่านไม่ได้เรื่องการใช้guid ของ

เช่น:

var myUniqueFileName = string.Format(@"{0}.txt", Guid.NewGuid());

หรือสั้นกว่า :

var myUniqueFileName = $@"{Guid.NewGuid()}.txt";

ในโปรแกรมของฉันบางครั้งฉันพยายามเช่น 10 ครั้งเพื่อสร้างชื่อที่อ่านได้ ("Image1.png" … "Image10.png") และหากล้มเหลว (เนื่องจากมีไฟล์อยู่แล้ว) ฉันจะกลับไปใช้ GUID

ปรับปรุง:

เมื่อเร็ว ๆ นี้ฉันยังใช้DateTime.Now.Ticksแทน GUID:

var myUniqueFileName = string.Format(@"{0}.txt", DateTime.Now.Ticks);

หรือ

var myUniqueFileName = $@"{DateTime.Now.Ticks}.txt";

ข้อดีสำหรับฉันคือการสร้างชื่อไฟล์ที่สั้นกว่าและ "ดูดีกว่า" เมื่อเทียบกับ GUID

โปรดทราบว่าในบางกรณี (เช่นเมื่อสร้างชื่อแบบสุ่มจำนวนมากในเวลาอันสั้น) อาจทำให้ค่าที่ไม่ซ้ำกัน

ปฏิบัติตาม GUID หากคุณต้องการให้แน่ใจว่าชื่อไฟล์ไม่ซ้ำกันแม้ว่าจะโอนไปยังคอมพิวเตอร์เครื่องอื่นก็ตาม


7
ฉันชอบใช้ Ticks เป็น GUID น่าเกลียดจริงๆ คุณยังสามารถรับแฮชของ Ticks ซึ่งช่วยลดความยาวอักขระของชื่อไฟล์ DateTime.Now.Ticks.GetHashCode().ToString("x").ToUpper()
WillMcKill

4
"เห็บ" สามารถคาดเดาได้และไม่ปลอดภัยต่อเธรด (เนื่องจาก 'เห็บ' เดียวกันสามารถรับได้จากหลายเธรด / กระบวนการ) ทำให้ไม่เหมาะสำหรับการสร้างชื่อไฟล์ชั่วคราว การสร้าง X..1..N อาจเหมาะสำหรับงานที่ต้องเผชิญกับผู้ใช้ (เช่นคัดลอกใน Explorer) แต่เป็นที่น่าสงสัยสำหรับการทำงานของเซิร์ฟเวอร์
user2864740

90

ใช้

Path.GetTempFileName()

หรือใช้ GUID ใหม่ ()

Path.GetTempFilename () บน MSDN


นี่คือลิงค์ไปยัง MSDN doc: msdn.microsoft.com/en-us/library/…
epotter

3
โปรดทราบว่าGetTempFileName()อาจทำให้เกิดข้อยกเว้นหากคุณสร้างไฟล์ดังกล่าวจำนวนมากโดยไม่ลบทิ้ง
Joey

21
"เมธอด GetTempFileName จะเพิ่มIOExceptionหากใช้เพื่อสร้างไฟล์มากกว่า 65535 ไฟล์โดยไม่ต้องลบไฟล์ชั่วคราวก่อนหน้านี้" บทความ MSDN กล่าว
Çağdaş Tekin

1
คำเตือน: GetTempFileNameจะสร้างไฟล์ นอกจากนี้ยังหมายความว่ามันเลือกที่ตั้งของเส้นทางชั่วคราว ในทางกลับกันGetRandomFileNameเหมาะที่จะสร้างชื่อไฟล์ 8.3 ที่สามารถใช้กับเส้นทางอื่นได้ (ฉันเคยเห็นโค้ดที่น่าสยดสยองที่ใช้ GetTempFileName กับไฟล์ลบเพียงเพื่อใช้ชื่อไฟล์ที่อื่น .. )
user2864740

77
System.IO.Path.GetRandomFileName()

Path.GetRandomFileName () บน MSDN


นี่คือลิงค์ไปยัง MSDN doc: msdn.microsoft.com/en-us/library/…
epotter

13
@RyBolt: เนื่องจากคุณไม่จำเป็นต้องเพาะเมล็ดด้วยตัวเองจึงไม่มีอะไรที่คุณต้องจำไว้สำหรับการใช้วิธีนั้น และฉันคาดหวังว่านักพัฒนาส่วนใหญ่จะไม่รู้ว่าจะสร้างระบบเข้ารหัสที่ปลอดภัยได้อย่างไร
Joey

54

หากความสามารถในการอ่านของชื่อไฟล์ไม่สำคัญ GUID ตามที่หลายคนแนะนำจะทำ อย่างไรก็ตามฉันพบว่าการค้นหาไดเรกทอรีที่มีชื่อไฟล์ 1,000 GUID นั้นเป็นเรื่องยากมากที่จะเรียงลำดับ ดังนั้นฉันจึงมักใช้การรวมกันของสตริงคงที่ซึ่งให้ชื่อไฟล์ข้อมูลบริบทการประทับเวลาและ GUID

ตัวอย่างเช่น:

public string GenerateFileName(string context)
{
    return context + "_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" + Guid.NewGuid().ToString("N");
}

filename1 = GenerateFileName("MeasurementData");
filename2 = GenerateFileName("Image");

วิธีนี้เมื่อฉันเรียงลำดับตามชื่อไฟล์ระบบจะจัดกลุ่มไฟล์โดยอัตโนมัติตามสตริงบริบทและจัดเรียงตามการประทับเวลา

โปรดทราบว่าขีด จำกัด ชื่อไฟล์ใน windows คือ 255 อักขระ


1
+1 สำหรับคำแนะนำในการรวมข้อมูลที่เป็นประโยชน์รวมกับ GUID - กันที่จะใช้กับเม็ดเกลือ: Right Click > Sort By > Dateรวมทั้งวันที่และเวลาในชื่อไฟล์เป็นชนิดของซ้ำซ้อนเมื่อคุณสามารถเพียงแค่
Timothy Shields

1
เวลาจะมีประโยชน์หากคุณจัดเก็บไฟล์จำนวนมากที่มีบริบทต่างกันในไดเรกทอรีเดียวกัน แน่นอนว่าการสร้างชื่อไฟล์ควรปรับเปลี่ยนตามความต้องการเฉพาะของคุณ
Mas

Guid.NewGuid().ToString();ควรจะเป็น ไม่มีวงเล็บ +1 มิฉะนั้น
Laurent W.

นี่เนียนมาก Timestamp และ Guid +1
JoshYates1980

ฉันชอบโซลูชันนี้ +1 ฉันเพิ่มส่วนขยายสตริงพารามิเตอร์ที่สองซึ่งฉันเพิ่มลงใน fileName สิ่งนี้สนับสนุนแนวคิดของบริบทเพิ่มเติมและช่วยให้เปิดไฟล์ด้วยแอปพลิเคชันเริ่มต้นได้อย่างง่ายดายด้วยการดับเบิลคลิกหากจำเป็น
shelbypereira

23

นี่คืออัลกอริทึมที่ส่งคืนชื่อไฟล์ที่อ่านได้เฉพาะตามต้นฉบับที่ให้มา หากมีไฟล์ต้นฉบับอยู่ไฟล์จะพยายามเพิ่มดัชนีต่อท้ายชื่อไฟล์จนกว่าจะพบไฟล์ที่ไม่มีอยู่ มันอ่านชื่อไฟล์ที่มีอยู่ลงใน HashSet เพื่อตรวจสอบการชนกันดังนั้นจึงค่อนข้างรวดเร็ว (ชื่อไฟล์ไม่กี่ร้อยต่อวินาทีบนเครื่องของฉัน) เธรดปลอดภัยด้วยและไม่ต้องทนทุกข์ทรมานจากสภาพการแข่งขัน

ตัวอย่างเช่นหากคุณส่งผ่านtest.txtมันจะพยายามสร้างไฟล์ตามลำดับนี้:

test.txt
test (2).txt
test (3).txt

ฯลฯ คุณสามารถระบุจำนวนครั้งสูงสุดหรือปล่อยให้เป็นค่าเริ่มต้น

นี่คือตัวอย่างที่สมบูรณ์:

class Program
{
    static FileStream CreateFileWithUniqueName(string folder, string fileName, 
        int maxAttempts = 1024)
    {
        // get filename base and extension
        var fileBase = Path.GetFileNameWithoutExtension(fileName);
        var ext = Path.GetExtension(fileName);
        // build hash set of filenames for performance
        var files = new HashSet<string>(Directory.GetFiles(folder));

        for (var index = 0; index < maxAttempts; index++)
        {
            // first try with the original filename, else try incrementally adding an index
            var name = (index == 0)
                ? fileName
                : String.Format("{0} ({1}){2}", fileBase, index, ext);

            // check if exists
            var fullPath = Path.Combine(folder, name);
            if(files.Contains(fullPath))
                continue;

            // try to create the file
            try
            {
                return new FileStream(fullPath, FileMode.CreateNew, FileAccess.Write);
            }
            catch (DirectoryNotFoundException) { throw; }
            catch (DriveNotFoundException) { throw; }
            catch (IOException) 
            {
                // Will occur if another thread created a file with this 
                // name since we created the HashSet. Ignore this and just
                // try with the next filename.
            } 
        }

        throw new Exception("Could not create unique filename in " + maxAttempts + " attempts");
    }

    static void Main(string[] args)
    {
        for (var i = 0; i < 500; i++)
        {
            using (var stream = CreateFileWithUniqueName(@"c:\temp\", "test.txt"))
            {
                Console.WriteLine("Created \"" + stream.Name + "\"");
            }
        }

        Console.ReadKey();
    }
}

ปลอดภัยไหม ไม่static readonly แปรผันไม่ใช่lockเหรอ?
Kiquenet

วิธีนี้เป็นแบบคงที่ดังนั้นฉันจึงเชื่อว่าหลายเธรดสามารถเข้าสู่วิธีนี้ได้อย่างปลอดภัยพร้อมกัน บางทีความปลอดภัยของเธรดอาจไม่ใช่คำที่ถูกต้อง - ฉันกำลังพยายามที่จะสื่อว่าหากเธรด / กระบวนการอื่นสร้างไฟล์ที่มีชื่อขัดแย้งกันในระหว่างการดำเนินการวิธีนี้จะกู้คืนและลองใช้ชื่อที่มีอยู่ถัดไป อย่าลังเลที่จะแก้ไขหากคุณคิดว่าสามารถปรับปรุงได้
Mike Chamberlain

บางที "การไม่ทุกข์ทรมานจากสภาพการแข่งขัน" เป็นวิธีที่ดีกว่าในการวาง
Mike Chamberlain

10

ฉันใช้GetRandomFileName :

เมธอด GetRandomFileName จะส่งคืนสตริงแบบสุ่มที่มีการเข้ารหัสที่แข็งแกร่งซึ่งสามารถใช้เป็นชื่อโฟลเดอร์หรือชื่อไฟล์ ต่างจาก GetTempFileName GetRandomFileName ไม่ได้สร้างไฟล์ เมื่อความปลอดภัยของระบบไฟล์ของคุณเป็นสิ่งสำคัญยิ่งควรใช้วิธีนี้แทน GetTempFileName

ตัวอย่าง:

public static string GenerateFileName(string extension="")
{
    return string.Concat(Path.GetRandomFileName().Replace(".", ""),
        (!string.IsNullOrEmpty(extension)) ? (extension.StartsWith(".") ? extension : string.Concat(".", extension)) : "");
}

เมธอด GetRandomFileName () สร้างชื่อไฟล์ที่ไม่ซ้ำกันทุกครั้งที่คล้ายกับ GUID () หรือไม่
Ashish Shukla

1
@AshishShukla ที่จริงฉันไม่รู้ msdnระบุว่า "สตริงสุ่มที่มีการเข้ารหัสที่แข็งแกร่ง" ถูกสร้างขึ้น ฉันไม่มีปัญหาจนถึงตอนนี้ หากเอกลักษณ์เป็นสิ่งสำคัญการตรวจสอบเพิ่มเติมอาจเป็นความคิดที่ดี
Koray

3
  1. สร้างชื่อไฟล์ที่ประทับเวลาของคุณตามกระบวนการปกติของคุณ
  2. ตรวจสอบว่ามีชื่อไฟล์หรือไม่
  3. เท็จ - บันทึกไฟล์
  4. True - ต่อท้ายอักขระเพิ่มเติมในไฟล์บางทีอาจเป็นตัวนับ
  5. ไปที่ขั้นตอนที่ 2

10
อัลกอริทึมนี้สามารถตรวจสอบการทำงานพร้อมกันได้
Jader Dias

3

คุณสามารถสร้างชื่อไฟล์ที่ไม่ซ้ำกันโดยอัตโนมัติสำหรับคุณโดยไม่ต้องใช้วิธีการที่กำหนดเอง เพียงแค่ใช้ต่อไปนี้กับชั้น StorageFolder หรือชั้น StorageFile กุญแจสำคัญคือ: CreationCollisionOption.GenerateUniqueNameและNameCollisionOption.GenerateUniqueName

ในการสร้างไฟล์ใหม่ที่มีชื่อไฟล์เฉพาะ:

var myFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("myfile.txt", NameCollisionOption.GenerateUniqueName);

ในการคัดลอกไฟล์ไปยังตำแหน่งที่มีชื่อไฟล์เฉพาะ:

var myFile2 = await myFile1.CopyAsync(ApplicationData.Current.LocalFolder, myFile1.Name, NameCollisionOption.GenerateUniqueName);

ในการย้ายไฟล์ที่มีชื่อไฟล์เฉพาะในตำแหน่งปลายทาง:

await myFile.MoveAsync(ApplicationData.Current.LocalFolder, myFile.Name, NameCollisionOption.GenerateUniqueName);

ในการเปลี่ยนชื่อไฟล์ด้วยชื่อไฟล์เฉพาะในตำแหน่งปลายทาง:

await myFile.RenameAsync(myFile.Name, NameCollisionOption.GenerateUniqueName);

2

ฉันใช้รหัสต่อไปนี้และใช้งานได้ดี ฉันหวังว่านี่อาจช่วยคุณได้

ฉันเริ่มต้นด้วยชื่อไฟล์เฉพาะโดยใช้การประทับเวลา -

"context_" + DateTime.Now ToString ("yyyyMMddHHmmssffff")

รหัส C # -

public static string CreateUniqueFile(string logFilePath, string logFileName, string fileExt)
    {
        try
        {
            int fileNumber = 1;

            //prefix with . if not already provided
            fileExt = (!fileExt.StartsWith(".")) ? "." + fileExt : fileExt;

            //Generate new name
            while (File.Exists(Path.Combine(logFilePath, logFileName + "-" + fileNumber.ToString() + fileExt)))
                fileNumber++;

            //Create empty file, retry until one is created
            while (!CreateNewLogfile(logFilePath, logFileName + "-" + fileNumber.ToString() + fileExt))
                fileNumber++;

            return logFileName + "-" + fileNumber.ToString() + fileExt;
        }
        catch (Exception)
        {
            throw;
        }
    }

    private static bool CreateNewLogfile(string logFilePath, string logFile)
    {
        try
        {
            FileStream fs = new FileStream(Path.Combine(logFilePath, logFile), FileMode.CreateNew);
            fs.Close();
            return true;
        }
        catch (IOException)   //File exists, can not create new
        {
            return false;
        }
        catch (Exception)     //Exception occured
        {
            throw;
        }
    }

1

คุณต้องการการประทับวันที่ในชื่อไฟล์หรือไม่?

คุณสามารถตั้งชื่อไฟล์เป็น GUID


@downvoter เหตุผลใดในการโหวตลง? GUID สำหรับชื่อไฟล์น่าจะเป็นคำตอบยอดนิยมสำหรับคำถามนี้
Larry Hipp

เป็นคำตอบที่ซ้ำแล้วซ้ำเล่าและฉันไม่มีชื่อเสียงพอที่จะ
โหวตลงคะแนน

@XMLforDummies คำตอบของฉันเป็นหนึ่งในคนแรก ตอนนี้อาจจะยังไม่เห็นเพราะเพิ่งแสดงชั่วโมงไปแล้ว เป็นคำตอบซ้ำ ๆ กันเพราะน่าจะเป็นคำตอบที่ถูกต้อง
Larry Hipp


1

วิธีการใช้Guid.NewGuid()เพื่อสร้าง GUID และใช้เป็นชื่อไฟล์ (หรือส่วนหนึ่งของชื่อไฟล์ร่วมกับการประทับเวลาของคุณหากคุณต้องการ)


1

ฉันได้เขียนฟังก์ชันเรียกซ้ำง่ายๆที่สร้างชื่อไฟล์เช่นเดียวกับ Windows โดยการต่อท้ายหมายเลขลำดับก่อนนามสกุลไฟล์

กำหนดเส้นทางไฟล์ที่ต้องการC:\MyDir\MyFile.txtและมีไฟล์อยู่แล้วจะส่งคืนไฟล์สุดท้ายของไฟล์C:\MyDir\MyFile_1.txt.

เรียกแบบนี้ว่า

var desiredPath = @"C:\MyDir\MyFile.txt";
var finalPath = UniqueFileName(desiredPath);

private static string UniqueFileName(string path, int count = 0)
{
    if (count == 0)
    {
        if (!File.Exists(path))
        {
            return path;
        }
    }
    else
    {
        var candidatePath = string.Format(
            @"{0}\{1}_{2}{3}",
            Path.GetDirectoryName(path),
            Path.GetFileNameWithoutExtension(path),
            count,
            Path.GetExtension(path));

        if (!File.Exists(candidatePath))
        {
            return candidatePath;
        }
    }

    count++;
    return UniqueFileName(path, count);
}

สิ่งนี้ไม่ปลอดภัยต่อเธรดหรือปลอดภัยในกระบวนการ มีเงื่อนไขการแข่งขันด้วยการตรวจสอบ File.Exists และการสร้างไฟล์ใด ๆ (ในภายหลัง) เล็กน้อยเมื่อเรียกสองครั้งติดต่อกันโดยไม่ต้องสร้างไฟล์ก็จะส่งคืนผลลัพธ์เดียวกัน
user2864740

1

เหตุใดเราจึงไม่สามารถสร้างรหัสเฉพาะตามด้านล่างนี้ได้

เราสามารถใช้ DateTime.Now.Ticks และ Guid.NewGuid (). ToString () เพื่อรวมเข้าด้วยกันและสร้าง id เฉพาะ

เมื่อเพิ่ม DateTime.Now.Ticks แล้วเราสามารถค้นหาวันที่และเวลาเป็นวินาทีที่สร้างรหัสเฉพาะได้

โปรดดูรหัส

var ticks = DateTime.Now.Ticks;
var guid = Guid.NewGuid().ToString();
var uniqueSessionId = ticks.ToString() +'-'+ guid; //guid created by combining ticks and guid

var datetime = new DateTime(ticks);//for checking purpose
var datetimenow = DateTime.Now;    //both these date times are different.

เรายังสามารถใช้ส่วนของเห็บใน id เฉพาะและตรวจสอบวันที่และเวลาในภายหลังเพื่อใช้อ้างอิงในอนาคต

คุณสามารถแนบ id เฉพาะที่สร้างขึ้นกับชื่อไฟล์หรือสามารถใช้สำหรับการสร้างรหัสเซสชันเฉพาะสำหรับการล็อกอิน - ล็อกเอาต์ของผู้ใช้ไปยังแอปพลิเคชันหรือเว็บไซต์ของเรา


ทำไมต้องกังวลกับ 'เห็บ' ถ้ามี GUID?
user2864740

ในระหว่างสถานการณ์ใด ๆ หากคุณต้องการตรวจสอบเมื่อมีการสร้าง uniqueSessionId คุณจะได้รับเวลาที่แน่นอน และที่เห็บเฉพาะนั้นจะเกิดขึ้นเพียงครั้งเดียวในชีวิต
Jineesh Uvantavida

ข้อสันนิษฐานเกี่ยวกับเห็บนั้นไม่ถูกต้อง: 1) ผู้สังเกตการณ์หลายคนสามารถเห็น 'เห็บ' เดียวกัน (คิดเธรด / กระบวนการ) และ 2) 'เห็บ' เดียวกันสามารถสังเกตได้หลายครั้งโดยผู้สังเกตคนเดียวกันหากสอบถามเร็วพอ
user2864740

อย่างไรก็ตามโดยเพียงแค่ใช้ Guid.NewGuid (และไม่สนใจว่าความเป็นจริงมันไม่ได้เป็น "cryptographically สุ่ม" ซึ่งอาจจะเป็นที่น่าสนใจในบางกรณี) เราสามารถยืนยันว่ามีความน่าจะเป็นสูงพอที่เราไม่สนใจเกี่ยวกับอย่างอื่น ID ที่ไม่ซ้ำกันจะ ถูกสร้างขึ้น - นี่เป็นการรับประกันที่สูงกว่า 'เห็บ'มาก ดังนั้น 'เห็บ' จึงไม่มีค่า / ใช้ในที่นี้คาดว่าจะเป็นข้อมูล "รอง" ที่ใส่ในชื่อไฟล์
user2864740

(FWIW: ฉันเพิ่งแก้ไขรหัสบางส่วนด้วยการยืนยันที่ไม่ได้กล่าวถึงข้างต้นเกี่ยวกับ 'เวลาที่ไม่ซ้ำกัน' .. )
user2864740

0

หากคุณต้องการให้วันที่เวลาชั่วโมงนาที ฯลฯ คุณสามารถใช้ตัวแปรคงที่ ผนวกค่าของตัวแปรนี้เข้ากับชื่อไฟล์ คุณสามารถเริ่มตัวนับด้วย 0 และเพิ่มขึ้นเมื่อคุณสร้างไฟล์ วิธีนี้ชื่อไฟล์จะไม่ซ้ำกันอย่างแน่นอนเนื่องจากคุณมีวินาทีในไฟล์ด้วย


0

ฉันมักจะทำอะไรบางอย่างตามบรรทัดเหล่านี้:

  • เริ่มต้นด้วยชื่อไฟล์ stem ( work.dat1เช่น)
  • ลองสร้างด้วย CreateNew
  • ถ้าได้ผลแสดงว่าคุณมีไฟล์ไม่เช่นนั้น ...
  • ผสมวันที่ / เวลาปัจจุบันลงในชื่อไฟล์ ( work.2011-01-15T112357.datเช่น)
  • พยายามสร้างไฟล์
  • หากได้ผลคุณมีไฟล์ไม่เช่นนั้น ...
  • ผสมตัวนับโมโนโทนิกเข้ากับชื่อไฟล์ ( work.2011-01-15T112357.0001.datเช่น (ฉันไม่ชอบ GUID ฉันชอบลำดับ / ความสามารถในการคาดเดา)
  • พยายามสร้างไฟล์ ทำเครื่องหมายที่เคาน์เตอร์และลองใหม่จนกว่าไฟล์จะถูกสร้างขึ้นสำหรับคุณ

นี่คือคลาสตัวอย่าง:

static class DirectoryInfoHelpers
{
    public static FileStream CreateFileWithUniqueName( this DirectoryInfo dir , string rootName )
    {
        FileStream fs = dir.TryCreateFile( rootName ) ; // try the simple name first

        // if that didn't work, try mixing in the date/time
        if ( fs == null )
        {
            string date = DateTime.Now.ToString( "yyyy-MM-ddTHHmmss" ) ;
            string stem = Path.GetFileNameWithoutExtension(rootName) ;
            string ext  = Path.GetExtension(rootName) ?? ".dat" ;

            ext = ext.Substring(1);

            string fn = string.Format( "{0}.{1}.{2}" , stem , date , ext ) ;
            fs = dir.TryCreateFile( fn ) ;

            // if mixing in the date/time didn't work, try a sequential search
            if ( fs == null )
            {
                int seq = 0 ;
                do
                {
                    fn = string.Format( "{0}.{1}.{2:0000}.{3}" , stem , date , ++seq , ext ) ;
                    fs = dir.TryCreateFile( fn ) ;
                } while ( fs == null ) ;
            }

        }

        return fs ;
    }

    private static FileStream TryCreateFile(this DirectoryInfo dir , string fileName )
    {
        FileStream fs = null ;
        try
        {
            string fqn = Path.Combine( dir.FullName , fileName ) ;

            fs = new FileStream( fqn , FileMode.CreateNew , FileAccess.ReadWrite , FileShare.None ) ;
        }
        catch ( Exception )
        {
            fs = null ;
        }
        return fs ;
    }

}

คุณอาจต้องการปรับแต่งอัลกอริทึม (เช่นใช้ส่วนประกอบที่เป็นไปได้ทั้งหมดกับชื่อไฟล์เสมอ) ขึ้นอยู่กับบริบท - ตัวอย่างเช่นหากฉันกำลังสร้างไฟล์บันทึกฉันอาจต้องการที่จะหมุนเวียนออกจากการดำรงอยู่คุณต้องการให้พวกเขาทั้งหมดแบ่งปันรูปแบบเดียวกันกับชื่อ

รหัสไม่สมบูรณ์ (เช่นไม่มีการตรวจสอบข้อมูลที่ส่งผ่าน) และอัลกอริทึมไม่สมบูรณ์แบบ (หากคุณเติมฮาร์ดไดรฟ์หรือพบสิทธิ์ข้อผิดพลาด I / O จริงหรือข้อผิดพลาดอื่น ๆ ของระบบไฟล์สิ่งนี้จะหยุดทำงานในขณะที่วนซ้ำไม่สิ้นสุด)


0

ฉันลงท้ายด้วยการเชื่อม GUID กับสตริงมิลลิวินาทีของวันเดือนปีที่สองและฉันคิดว่าโซลูชันนี้ค่อนข้างดีในสถานการณ์ของฉัน



0

ฉันเขียนคลาสสำหรับทำสิ่งนี้โดยเฉพาะ เริ่มต้นด้วยส่วน "ฐาน" (ค่าเริ่มต้นคือการประทับเวลาที่แม่นยำเป็นนาที) และหลังจากนั้นจะต่อท้ายตัวอักษรเพื่อสร้างชื่อที่ไม่ซ้ำกัน ดังนั้นหากตราประทับแรกที่สร้างขึ้นคือ 1907101215a ที่สองจะเป็น 1907101215b จากนั้น 1907101215c และอื่น ๆ

หากฉันต้องการตราประทับที่ไม่ซ้ำกันมากกว่า 25 ดวงฉันจะใช้ unary 'z เพื่อนับ 25 ดังนั้นมันจะไป 1907101215y, 1907101215za, 1907101215zb, ... 1907101215zy, 1907101215zza, 1907101215zzb และอื่น ๆ สิ่งนี้รับประกันได้ว่าแสตมป์จะเรียงลำดับตัวอักษรและตัวเลขตามลำดับที่สร้างขึ้นเสมอ (ตราบใดที่อักขระตัวถัดไปหลังจากตราประทับไม่ใช่ตัวอักษร)

มันไม่ปลอดภัยต่อเธรดไม่อัปเดตเวลาโดยอัตโนมัติและขยายอย่างรวดเร็วหากคุณต้องการตราประทับหลายร้อยดวง แต่ฉันพบว่าเพียงพอสำหรับความต้องการของฉัน

/// <summary>
/// Class for generating unique stamps (for filenames, etc.)
/// </summary>
/// <remarks>
/// Each time ToString() is called, a unique stamp is generated.
/// Stamps are guaranteed to sort alphanumerically in order of generation.
/// </remarks>
public class StampGenerator
{
  /// <summary>
  /// All the characters which could be the last character in the stamp.
  /// </summary>
  private static readonly char[] _trailingChars =
  {
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
    'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
    'u', 'v', 'w', 'x', 'y'
  };

  /// <summary>
  /// How many valid trailing characters there are.
  /// </summary>
  /// <remarks>Should always equal _trailingChars.Length</remarks>
  public const int TRAILING_RANGE = 25;

  /// <summary>
  /// Maximum length of the stamp. Hard-coded for laziness.
  /// </summary>
  public const int MAX_LENGTH_STAMP = 28;

  /// <summary>
  /// Base portion of the stamp. Will be constant between calls.
  /// </summary>
  /// <remarks>
  /// This is intended to uniquely distinguish between instances.
  /// Default behavior is to generate a minute-accurate timestamp.
  /// </remarks>
  public string StampBase { get; }

  /// <summary>
  /// Number of times this instance has been called.
  /// </summary>
  public int CalledTimes { get; private set; }

  /// <summary>
  /// Maximum number of stamps that can be generated with a given base.
  /// </summary>
  public int MaxCalls { get; }

  /// <summary>
  /// Number of stamps remaining for this instance.
  /// </summary>
  public int RemainingCalls { get { return MaxCalls - CalledTimes; } }

  /// <summary>
  /// Instantiate a StampGenerator with a specific base.
  /// </summary>
  /// <param name="stampBase">Base of stamp.</param>
  /// <param name="calledTimes">
  /// Number of times this base has already been used.
  /// </param>
  public StampGenerator(string stampBase, int calledTimes = 0)
  {
    if (stampBase == null)
    {
      throw new ArgumentNullException("stampBase");
    }
    else if (Regex.IsMatch(stampBase, "[^a-zA-Z_0-9 \\-]"))
    {
      throw new ArgumentException("Invalid characters in Stamp Base.",
                                  "stampBase");
    }
    else if (stampBase.Length >= MAX_LENGTH_STAMP - 1)
    {
      throw new ArgumentException(
        string.Format("Stamp Base too long. (Length {0} out of {1})",
                      stampBase.Length, MAX_LENGTH_STAMP - 1), "stampBase");
    }
    else if (calledTimes < 0)
    {
      throw new ArgumentOutOfRangeException(
        "calledTimes", calledTimes, "calledTimes cannot be negative.");
    }
    else
    {
      int maxCalls = TRAILING_RANGE * (MAX_LENGTH_STAMP - stampBase.Length);
      if (calledTimes >= maxCalls)
      {
        throw new ArgumentOutOfRangeException(
          "calledTimes", calledTimes, string.Format(
            "Called Times too large; max for stem of length {0} is {1}.",
            stampBase.Length, maxCalls));
      }
      else
      {
        StampBase = stampBase;
        CalledTimes = calledTimes;
        MaxCalls = maxCalls;
      }
    }
  }

  /// <summary>
  /// Instantiate a StampGenerator with default base string based on time.
  /// </summary>
  public StampGenerator() : this(DateTime.Now.ToString("yMMddHHmm")) { }

  /// <summary>
  /// Generate a unique stamp.
  /// </summary>
  /// <remarks>
  /// Stamp values are orered like this:
  /// a, b, ... x, y, za, zb, ... zx, zy, zza, zzb, ...
  /// </remarks>
  /// <returns>A unique stamp.</returns>
  public override string ToString()
  {
    int zCount = CalledTimes / TRAILING_RANGE;
    int trailing = CalledTimes % TRAILING_RANGE;
    int length = StampBase.Length + zCount + 1;

    if (length > MAX_LENGTH_STAMP)
    {
      throw new InvalidOperationException(
        "Stamp length overflown! Cannot generate new stamps.");
    }
    else
    {
      CalledTimes = CalledTimes + 1;
      var builder = new StringBuilder(StampBase, length);
      builder.Append('z', zCount);
      builder.Append(_trailingChars[trailing]);
      return builder.ToString();
    }
  }
}

-1

DateTime.Now.Ticksไม่ปลอดภัยGuid.NewGuid()น่าเกลียดเกินไปหากคุณต้องการสิ่งที่สะอาดและเกือบปลอดภัย ( ไม่ปลอดภัย 100% เช่นถ้าคุณเรียกมันว่า 1,000,000 ครั้งใน 1 มิลลิวินาที ) ให้ลอง:

Math.Abs(Guid.NewGuid().GetHashCode())

โดยปลอดภัยฉันปลอดภัยหมายถึงจะไม่ซ้ำกันเมื่อคุณเรียกมันดังนั้นหลายครั้งในระยะเวลาอันสั้นMS ไม่กี่ของเวลา


มีปัญหาใด ๆ กับการลงคะแนนเสียงของโซลูชันของฉันหรือไม่? โปรดแจ้งให้เราทราบ
Mehdi Dehghani

GetHashCodeวิธีการส่งกลับintซึ่งมีช่วง 32 บิตในขณะที่GUIDมีช่วง 128 บิตและเพื่อให้เป็นมากมีแนวโน้มที่จะไม่ซ้ำกัน หากคุณไม่ชอบรูปแบบของGUIDค่าเพียงแค่เรียกToString("N")ใช้ซึ่งจะลบเครื่องหมายขีดกลาง
4thex
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.