Cách tạo tên tệp duy nhất trong C #


131

Tôi đã thực hiện một thuật toán sẽ tạo các tên duy nhất cho các tệp sẽ lưu trên ổ cứng. Tôi đang nối thêm DateTime: Giờ, Phút, Giây và Mili giây nhưng vẫn tạo ra tên trùng lặp của các tệp vì tôi đang tải lên nhiều tệp cùng một lúc.

Giải pháp tốt nhất để tạo tên duy nhất cho các tệp được lưu trữ trên ổ cứng để không có 2 tệp nào giống nhau?


Nó phụ thuộc vào các yêu cầu khác; câu hỏi [cũ] này là / quá mơ hồ.
dùng2864740

Câu trả lời:


240

Nếu khả năng đọc không thành vấn đề, hãy sử dụng GUID .

Ví dụ:

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

hoặc ngắn hơn :

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

Trong các chương trình của mình, đôi khi tôi thử ví dụ 10 lần để tạo một tên có thể đọc được ("Image1.png", "Image10.png") và nếu thất bại (vì tệp đã tồn tại), tôi quay lại GUID.

Cập nhật:

Gần đây, tôi cũng đã sử dụng DateTime.Now.Ticksthay vì GUID:

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

hoặc là

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

Lợi ích đối với tôi là điều này tạo ra một tên tệp ngắn hơn và "đẹp hơn", so với GUID.

Xin lưu ý rằng trong một số trường hợp (ví dụ: khi tạo nhiều tên ngẫu nhiên trong một thời gian rất ngắn), điều này có thể tạo ra các giá trị không duy nhất.

Bám sát GUID nếu bạn muốn thực sự chắc chắn rằng tên tệp là duy nhất, ngay cả khi chuyển chúng sang các máy tính khác.


7
Tôi thích sử dụng Ticks như một GUID thực sự xấu xí. Bạn cũng có thể nhận được một hàm Băm làm giảm độ dài ký tự của tên tệp. DateTime.Now.Ticks.GetHashCode().ToString("x").ToUpper()
WillMcKill

4
"Ticks" có thể dự đoán được và không an toàn cho luồng (vì cùng một "dấu tích" có thể thu được từ nhiều luồng / quy trình). Điều này làm cho nó không phù hợp để tạo tên tệp tạm thời. Tạo X..1..N có thể phù hợp với các tác vụ của người dùng (ví dụ: sao chép trong Explorer), nhưng không rõ ràng cho công việc của máy chủ.
dùng2864740

90

Sử dụng

Path.GetTempFileName()

hoặc sử dụng GUID mới ().

Path.GetTempFilename () trên MSDN .


Đây là liên kết đến tài liệu MSDN: msdn.microsoft.com/en-us/l
Library / 2007

3
Lưu ý rằng, điều đó GetTempFileName()có thể ném một ngoại lệ nếu bạn tạo nhiều tệp như vậy mà không xóa chúng.
Joey

21
"Phương thức GetTempFileName sẽ nâng cao IOException nếu nó được sử dụng để tạo hơn 65535 tệp mà không xóa các tệp tạm thời trước đó." bài báo MSDN.
Tekağdaş Tekin

1
CẢNH BÁO: GetTempFileNamesẽ tạo một Tệp. Điều này cũng có nghĩa là nó chọn vị trí đường dẫn tạm thời . Mặt khác, GetRandomFileNamephù hợp để tạo một tên tệp 8.3 có thể được sử dụng với một đường dẫn khác. (Tôi đã thấy một số mã khủng khiếp sử dụng GetTempFileName với File.Delete chỉ để sử dụng tên tệp ở nơi khác ..)
user2864740


54

Nếu khả năng đọc của tên tệp không quan trọng, thì GUID, như được đề xuất bởi nhiều người sẽ làm. Tuy nhiên, tôi thấy rằng việc xem xét một thư mục có 1000 tên tệp GUID rất khó để sắp xếp. Vì vậy, tôi thường sử dụng kết hợp một chuỗi tĩnh cung cấp cho tên tệp một số thông tin ngữ cảnh, dấu thời gian và GUID.

Ví dụ:

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

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

Bằng cách này, khi tôi sắp xếp theo tên tệp, nó sẽ tự động nhóm các tệp theo chuỗi ngữ cảnh và sắp xếp theo dấu thời gian.

Lưu ý rằng giới hạn tên tệp trong windows là 255 ký tự.


1
+1 Đối với đề xuất bao gồm thông tin hữu ích kết hợp với GUID. - Bỏ qua một hạt muối: bao gồm ngày và thời gian trong tên tệp là loại dư thừa khi bạn có thể Right Click > Sort By > Date.
Timothy Shields

1
Thời gian sẽ trở nên hữu ích nếu bạn đang lưu trữ một loạt các tệp với các bối cảnh khác nhau trong cùng một thư mục. Tất nhiên, việc tạo tên tệp nên được điều chỉnh dựa trên nhu cầu cụ thể của bạn.
Mas

Nên Guid.NewGuid().ToString();. Thiếu dấu ngoặc đơn. +1 khác
Laurent W.

Điều này rất trơn tru. Dấu thời gian và hướng dẫn. +1
JoshYates1980

Tôi thích giải pháp này +1, tôi đã thêm một phần mở rộng chuỗi tham số thứ hai mà tôi thêm vào fileName, điều này hỗ trợ thêm cho ý tưởng về bối cảnh và cho phép dễ dàng mở tệp bằng ứng dụng mặc định khi nhấp đúp nếu cần
Shelbypereira

23

Đây là một thuật toán trả về một tên tệp có thể đọc được dựa trên bản gốc được cung cấp. Nếu tệp gốc tồn tại, nó sẽ cố gắng thêm chỉ mục vào tên tệp cho đến khi tìm thấy tệp không tồn tại. Nó đọc tên tệp hiện có vào Hashset để kiểm tra va chạm nên khá nhanh (vài trăm tên tệp mỗi giây trên máy của tôi), nó cũng an toàn và không bị điều kiện cuộc đua.

Ví dụ: nếu bạn vượt qua nó test.txt, nó sẽ cố gắng tạo các tệp theo thứ tự này:

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

v.v. Bạn có thể chỉ định các lần thử tối đa hoặc chỉ để mặc định.

Đây là một ví dụ hoàn chỉnh:

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();
    }
}

an toàn chủ đề ? khôngstatic readonly biến cũng khônglock?
Kiquenet

Bản thân phương thức này là tĩnh nên không chia sẻ gì, vì vậy tôi tin rằng nhiều luồng có thể nhập đồng thời phương thức này một cách an toàn. Có lẽ luồng an toàn không phải là thuật ngữ chính xác - Tôi đang cố gắng truyền đạt rằng nếu một luồng / tiến trình khác tạo một tệp có tên xung đột trong khi thực thi thì phương thức này sẽ phục hồi và thử tên có sẵn tiếp theo. Hãy chỉnh sửa nếu bạn nghĩ rằng nó có thể được cải thiện.
Mike Chamberlain

Có lẽ "không phải chịu một điều kiện chủng tộc" là một cách tốt hơn để đặt nó.
Mike Chamberlain

10

Tôi sử dụng GetRandomFileName :

Phương thức GetRandomFileName trả về một chuỗi ngẫu nhiên, mã hóa mạnh, có thể được sử dụng làm tên thư mục hoặc tên tệp. Không giống như GetTempFileName, GetRandomFileName không tạo tệp. Khi bảo mật hệ thống tệp của bạn là tối quan trọng, phương pháp này nên được sử dụng thay cho GetTempFileName.

Thí dụ:

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

Phương thức GetRandomFileName () có luôn tạo tên tệp duy nhất mỗi lần tương tự như GUID () không?
Ashish Shukla

1
@AshishShukla thực sự tôi không có ý kiến ​​gì. msdn nói "một chuỗi ngẫu nhiên, mạnh mẽ về mật mã" được tạo ra. Tôi không có vấn đề cho đến nay. Nếu tính duy nhất là quan trọng, kiểm tra thêm có thể là một ý tưởng tốt.
Koray

3
  1. Tạo tên tệp được đánh dấu thời gian theo quy trình thông thường của bạn
  2. Kiểm tra xem tên tệp có tồn tại không
  3. Sai - lưu tệp
  4. Đúng - Nối thêm ký tự vào tệp, có thể là bộ đếm
  5. Chuyển đến bước 2

10
Thuật toán này phù hợp với sự tương tranh
Jader Dias

3

Bạn có thể có một tên tệp duy nhất được tạo tự động cho bạn mà không cần bất kỳ phương thức tùy chỉnh nào. Chỉ cần sử dụng các mục sau với Lớp StorageFolder hoặc Lớp StorageFile . Chìa khóa ở đây là: CreationCollisionOption.GenerateUniqueNameNameCollisionOption.GenerateUniqueName

Để tạo một tệp mới với một tên tệp duy nhất:

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

Để sao chép tệp vào một vị trí có tên tệp duy nhất:

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

Để di chuyển tệp có tên tệp duy nhất ở vị trí đích:

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

Để đổi tên một tệp có tên tệp duy nhất ở vị trí đích:

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

2

Tôi đã sử dụng mã sau đây và nó hoạt động tốt. Tôi hy vọng điều này có thể giúp bạn.

Tôi bắt đầu với một tên tệp duy nhất bằng dấu thời gian -

"bối cảnh_" + DateTime.Now.ToString ("yyyyMMddHHmmssffff")

Mã 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

Bạn có cần dấu thời gian ngày trong tên tệp?

Bạn có thể đặt tên tệp là GUID.


@downvoter Bất kỳ lý do cho việc bỏ phiếu xuống? GUID cho tên tệp dường như là một câu trả lời phổ biến cho câu hỏi này.
Larry Hipp

Đó là một câu trả lời lặp đi lặp lại và tôi không đủ danh tiếng để hạ bệ
Jader Dias

@XMLforDummies câu trả lời của tôi là một trong những câu hỏi đầu tiên. Nó có thể không giống như bây giờ bởi vì nó chỉ hiển thị giờ. Đó là một câu trả lời lặp đi lặp lại vì có lẽ đó là câu trả lời đúng.
Larry Hipp


1

Cách sử dụng Guid.NewGuid()để tạo GUID và sử dụng tên đó làm tên tệp (hoặc một phần của tên tệp cùng với dấu thời gian của bạn nếu bạn muốn).


1

Tôi đã viết một hàm đệ quy đơn giản để tạo tên tệp như Windows, bằng cách nối thêm số thứ tự trước phần mở rộng tệp.

Đưa ra một đường dẫn tệp mong muốn C:\MyDir\MyFile.txtvà tệp đã tồn tại, nó trả về đường dẫn tệp cuối cùng của C:\MyDir\MyFile_1.txt.

Nó được gọi như thế này:

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);
}

Đây không phải là an toàn chủ đề hoặc an toàn quá trình. Có một điều kiện cuộc đua với kiểm tra File.Exists và bất kỳ việc tạo (sau này được cho là) ​​của tệp. Một cách tầm thường, khi được gọi hai lần liên tiếp mà không tạo tệp, nó sẽ trả về cùng một kết quả.
dùng2864740

1

Tại sao chúng ta không thể tạo một id duy nhất như dưới đây.

Chúng tôi có thể sử dụng DateTime.Now.Ticks và Guid.NewGuid (). ToString () để kết hợp với nhau và tạo một id duy nhất.

Khi DateTime.Now.Ticks được thêm vào, chúng ta có thể tìm ra Ngày và Giờ tính bằng giây mà id duy nhất được tạo.

Xin vui lòng xem mã.

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.

Chúng tôi thậm chí có thể lấy một phần của bọ ve trong id duy nhất và kiểm tra ngày và thời gian sau để tham khảo trong tương lai.

Bạn có thể đính kèm id duy nhất được tạo vào tên tệp hoặc có thể được sử dụng để tạo id phiên duy nhất để đăng xuất đăng nhập của người dùng vào ứng dụng hoặc trang web của chúng tôi.


Tại sao phải bận tâm với 'tick' nếu có GUID?
dùng2864740

Trong bất kỳ kịch bản nào, nếu bạn cần kiểm tra khi uniqueSessionId được tạo, bạn sẽ có được thời gian chính xác. Và cũng tại đó, đánh dấu cụ thể sẽ chỉ xảy ra một lần trong đời.
Jineesh Uaugeavida

Một cách tầm thường, giả định đó về bọ ve là không hợp lệ: 1) nhiều người quan sát có thể thấy cùng một 'đánh dấu' (nghĩ chủ đề / quy trình) và 2) cùng một 'đánh dấu' có thể được quan sát nhiều lần bởi cùng một người quan sát, nếu được truy vấn đủ nhanh.
dùng2864740

Tuy nhiên, bằng cách chỉ sử dụng Guid.NewGuid (và bỏ qua thực tế đó không phải là "ngẫu nhiên về mật mã" có thể được quan tâm trong một số trường hợp), chúng tôi có thể khẳng định rằng, với xác suất đủ cao mà chúng tôi không quan tâm nếu không, một ID duy nhất sẽ được tạo ra - đây là một đảm bảo cao hơn nhiều so với 'tick' . Do đó, 'tick' không có giá trị / sử dụng ở đây mong muốn dữ liệu "thứ cấp" được đưa vào tên tệp.
dùng2864740

(FWIW: Tôi vừa sửa một số mã với một xác nhận đã nói ở trên về 'thời gian duy nhất' ..)
user2864740

0

Nếu bạn muốn có datetime, giờ, phút, v.v. bạn có thể sử dụng biến tĩnh. Nối giá trị của biến này vào tên tệp. Bạn có thể bắt đầu bộ đếm với 0 và tăng dần khi bạn đã tạo một tệp. Bằng cách này, tên tệp chắc chắn sẽ là duy nhất vì bạn cũng có vài giây trong tệp.


0

Tôi thường làm một cái gì đó dọc theo những dòng này:

  • bắt đầu với một tên tập tin gốc ( work.dat1ví dụ)
  • cố gắng tạo nó với Tạo mới
  • nếu nó hoạt động, bạn đã có tập tin, nếu không ...
  • trộn ngày / giờ hiện tại vào tên tệp ( work.2011-01-15T112357.datví dụ)
  • cố gắng tạo tập tin
  • nếu nó hoạt động, bạn đã có tập tin, nếu không ...
  • Trộn một bộ đếm đơn điệu vào tên tệp (work.2011-01-15T112357.0001.dat ví dụ: (Tôi không thích GUID. Tôi thích thứ tự / dự đoán.)
  • cố gắng tạo tập tin Tiếp tục đánh dấu vào quầy và thử lại cho đến khi một tập tin được tạo cho bạn.

Đây là một lớp mẫu:

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 ;
    }

}

Bạn có thể muốn điều chỉnh thuật toán (luôn luôn sử dụng tất cả các thành phần có thể cho tên tệp). Phụ thuộc vào ngữ cảnh - Ví dụ: nếu tôi đang tạo tệp nhật ký, tôi có thể muốn thoát khỏi sự tồn tại, bạn muốn tất cả chúng chia sẻ cùng một mẫu với tên.

Mã không hoàn hảo (chẳng hạn kiểm tra dữ liệu được truyền vào). Và thuật toán không hoàn hảo (ví dụ, nếu bạn điền vào ổ cứng hoặc gặp phải quyền, lỗi I / O thực tế hoặc các lỗi hệ thống tệp khác, ví dụ, điều này sẽ bị treo, vì nó đứng, trong một vòng lặp vô hạn).


0

Tôi kết thúc việc kết hợp GUID với chuỗi ngày tháng năm thứ hai chuỗi thứ hai và tôi nghĩ giải pháp này khá tốt trong kịch bản của tôi



0

Tôi đã viết một lớp học đặc biệt để làm điều này. Nó được khởi tạo với một phần "cơ sở" (mặc định là dấu thời gian chính xác từng phút) và sau đó thêm các chữ cái để tạo ra các tên duy nhất. Vì vậy, nếu tem đầu tiên được tạo ra là 1907101215a, tem thứ hai sẽ là 1907101215b, sau đó là 1907101215c, et cetera.

Nếu tôi cần nhiều hơn 25 tem độc đáo thì tôi sử dụng số đơn vị để đếm 25. Vì vậy, nó đi 1907101215y, 1907101215za, 1907101215zb, ... 1907101215zy, 1907101215zza, 1907101215zzb, v.v. Điều này đảm bảo rằng các tem sẽ luôn sắp xếp chữ và số theo thứ tự chúng được tạo ra (miễn là ký tự tiếp theo sau tem không phải là một chữ cái).

Nó không an toàn cho chủ đề, không tự động cập nhật thời gian và nhanh chóng nở nếu bạn cần hàng trăm tem, nhưng tôi thấy nó đủ cho nhu cầu của tôi.

/// <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.Tickskhông an toàn, Guid.NewGuid()quá xấu, nếu bạn cần thứ gì đó sạch sẽ và gần như an toàn ( ví dụ như nó không an toàn 100% nếu bạn gọi nó là 1.000.000 lần trong 1ms ), hãy thử:

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

Bởi an toàn tôi có nghĩa là an toàn là duy nhất khi bạn gọi nó rất nhiều lần trong khoảng thời gian rất ngắn vài ms thời gian.


Có bất kỳ vấn đề với downvoter giải pháp của tôi? làm ơn cho tôi biết.
Mehdi Dehghani

Các GetHashCodephương thức trả về một int, trong đó có khoảng 32-bit, trong khi một GUIDcó một phạm vi 128-bit, và như vậy là nhiều khả năng là duy nhất. Nếu bạn không thích định dạng của một GUIDgiá trị, chỉ cần gọi ToString("N")nó để xóa dấu gạch ngang.
Thứ 4
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.