Click here to Skip to main content
Click here to Skip to main content

Format File Sizes in Human Readable Format

By , 30 Nov 2002
 

Format Size Test Application

Introduction

While working on a project I needed to display the sizes in a human readable format, i.e. 1KB, 1.20MB, and so on. Although I am referring to this functionality here as file size formatting, the same code can be used to format any value that represents a number of bytes. Due to the simplicity of the problem, I decided that this would be a perfect topic for my first article!

Requirements

  • Using B, KB, MB, GB to display the sizes in a logical format.
  • Using commas as thousands separator

An example would be: 1,023 KB (if size = 1023 * 1024 bytes)

Using the code

The code is composed of two functions. The first is InsertSeparators() which is used internally from within the main function to add the thousands separator. The second and main function is FormatSize() which is used to do the all the required formatting.

Following is the code for InsertSeparators(). This function is placed in an unnamed namespace in order to make it a local function.

namespace
{
  /**
   * Converts a positive number to a string while inserting separators.
   *
   * @param dwNumber A positive number to add thousands separator for
   *
   * @return The number with thousand separators as a CString
   */
  CString InsertSeparator (DWORD dwNumber)
  {
    CString str;

    str.Format("%u", dwNumber);

    for (int i = str.GetLength()-3; i > 0; i -= 3)
    {
      str.Insert(i, ",");
    }

    return (str);
  }
}

Following is the code for the FormatSize() function:

/**
 * Converts a filesize to a human readable format.
 *
 * This involves the use of K and M as multipliers.  Hence,
 * strings are formatted as 1024 -> 1KB, 1023 -> 1,023 B,
 * and so on.
 *
 * @param dwFileSize File size to be formatted.
 *
 * @return The formatted filesize as a CString
 */
CString FormatSize (DWORD dwFileSize)
{
  static const DWORD dwKB = 1024;          // Kilobyte
  static const DWORD dwMB = 1024 * dwKB;   // Megabyte
  static const DWORD dwGB = 1024 * dwMB;   // Gigabyte

  DWORD dwNumber, dwRemainder;
  CString strNumber;

  if (dwFileSize < dwKB)
  {
    strNumber = InsertSeparator(dwFileSize) + " B";
  } 
  else
  {
    if (dwFileSize < dwMB)
    {
      dwNumber = dwFileSize / dwKB;
      dwRemainder = (dwFileSize * 100 / dwKB) % 100;

      strNumber.Format("%s.%02d KB", (LPCSTR)InsertSeparator(dwNumber), dwRemainder);
    }
    else
    {
      if (dwFileSize < dwGB)
      {
        dwNumber = dwFileSize / dwMB;
        dwRemainder = (dwFileSize * 100 / dwMB) % 100;
        strNumber.Format("%s.%02d MB", InsertSeparator(dwNumber), dwRemainder);
      }
      else
      {
        if (dwFileSize >= dwGB)
        {
          dwNumber = dwFileSize / dwGB;
          dwRemainder = (dwFileSize * 100 / dwGB) % 100;
          strNumber.Format("%s.%02d GB", InsertSeparator(dwNumber), dwRemainder);
        }
      }
    }
  }

  // Display decimal points only if needed
  // another alternative to this approach is to check before calling str.Format, and 
  // have separate cases depending on whether dwRemainder == 0 or not.
  strNumber.Replace(".00", "");

  return strNumber;
}

Future Improvements

The following are planned future improvements for the next versions of the article:

  • Make the thousands separator and the decimal point dependent on Control Panel settings.
  • Unicode Support

History

01-DEC-2002First version

Edit History

2 Dec 2002 - Initial Edit

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Victor Boctor
Web Developer
United States United States
Member
Victor Boctor is a Windows/Linux professional developer. He has worked in the fields of Email Servers, Automatic Test Equipment, Telecommunications, and Embroidery software. He developed software using C#, C++, MFC, Win32, PHP, Cold Fusion, HTML, XHTML, and SQL.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralGeneralize SolutionmemberMina Victor4 Jan '10 - 14:14 
I think this would generalize the solution Smile | :)

size_t formatter(double dNumber, TCHAR* pszBuffer, size_t nSize) {
::ZeroMemory(pszBuffer, nSize);
int iDepth = 0;
 
double dResult = _formatter(dNumber, iDepth);
_stprintf(pszBuffer, _T("%.02f"), dResult);
switch (iDepth) {
case 0: _tcscat(pszBuffer, _T(" B") );break;
case 1: _tcscat(pszBuffer, _T(" KB") ); break;
case 2: _tcscat(pszBuffer, _T(" MB") ); break;
case 3: _tcscat(pszBuffer, _T(" GB") ); break;
case 4: _tcscat(pszBuffer, _T(" TB") ); break;
case 5: _tcscat(pszBuffer, _T(" PB") ); break;
case 6: _tcscat(pszBuffer, _T(" EB") ); break;
}
 
return _tcslen(pszBuffer);
}
 
double _formatter(double dNumber, int& iDepth) {
if (dNumber < 1024.0) {
return dNumber;
} else {
return _formatter(dNumber / 1024.0, ++iDepth);
}
}

 
---
Mina A. Victor
GeneralEmbroiderymembermmhafez17 Sep '03 - 6:54 
Hello,
 
i'm sorry, i'm not sending u about ur project, but i didn't find ur email to contact u. i'm interrested in embroidery, i'm implementing a project in embroiderey and i need ur help plz.
i want to implement code to read and write embroidery files. so would u plz send me any available embroidery files formats to implement them.
to contact me : mmhafez@hotmail.com
thx for ur cooperation
 
yours sincerely Confused | :confused:
 
Eng. Mohamed M. Hafez
GeneralAlso see GetNumberFormat()memberRavi Bhavnani1 Dec '02 - 6:35 
This is a useful article! But you may want to consider using ::GetNumberFormat().
 
/ravi
 
Let's put "civil" back in "civilization"
http://www.ravib.com
ravib@ravib.com
GeneralAnother waymemberdabs1 Dec '02 - 2:16 
Nice article, congratulations!
 
There is another way to accomplish this if you are willing to link to shlwapi.lib (and therefore create a dependency on shlwapi.dll). It contains the function StrFormatByteSize(DWORD dw, LPSTR pszBuf, UINT cchBuf ); which I believe does at least very similar job.
 
The functions inside shlwapi.dll are often overlooked - there are some very handy functions in there!

 



Wenn ist das Nunstück git und Slotermeyer? Ja! Beierhund das oder die Flipperwaldt gersput!
GeneralRe: Another waymemberVictor Boctor1 Dec '02 - 2:31 
dabs wrote:
StrFormatByteSize(DWORD dw, LPSTR pszBuf, UINT cchBuf );
 
Thanks for the hint. I was sure there must be a standard way to do this Wink | ;) . However, the API does not provide the possibility to control the format, which is possible through altering the above code. I am also planning to provide some customisation features in future versions Cool | :cool:
 
Some compatability information from MSDN (there is no mention of XP or Me):
shlwapi.dll version 4.71 or later
Windows 2000, Windows NT 4.0 with Internet Explorer 4.0, Windows 98, Windows 95 with Internet Explorer 4.0
 
Regards,
Victor
 
phpWebNotes is a page annotation system modelled after php.net.
http://webnotes.sourceforge.net/demo.php[^]
GeneralRe: Another waymemberAndreas Saurwein4 Dec '02 - 0:24 
MSDN say:
Note 2: All systems with Internet Explorer 4.0 or 4.01 will have the associated version of Comctl32.dll and Shlwapi.dll (4.71 or 4.72, respectively). However, for systems prior to Windows 98, Internet Explorer 4.0 and 4.01 can be installed with or without the integrated Shell. If they are installed with the integrated Shell, the associated version of Shell32.dll will be installed. If they are installed without the integrated Shell, Shell32.dll is not updated. In other words, the presence of version 4.71 or 4.72 of Comctl32.dll or Shlwapi.dll on a system does not guarantee that Shell32.dll has the same version number. All Windows 98 systems have version 4.72 of Shell32.dll.
 
So, any system with at least IE4.0 will have that function available.
 
Victor Boctor wrote:
However, the API does not provide the possibility to control the format, which is possible through altering the above code.
 
For what? There is a standard way to do it. Not that I want to suppress the value of your code, but someone (MS) has done it already. And it seems to work pretty fine.
And it always produces the same result as seen in Windows Explorer.
 
There are, by the way, 3 functions to accomplish this: StrFormatByteSize(), StrFormatByteSize64() and StrFormatKBSize().

 

I don't think this is a serious possesion, and the evil most likely comes from your hand. Colin J Davies, The Lounge


GeneralNice, but...memberAnders Molin1 Dec '02 - 1:02 
Instead of just using "," as thousand separator, it would be great if you took it from windows' locale settings.
 
Where I live we use "." as thousand separator Wink | ;)
 
- Anders
 
Money talks, but all mine ever says is "Goodbye!"
GeneralRe: Nice, but...memberVictor Boctor1 Dec '02 - 1:11 
Anders Molin wrote:
Instead of just using "," as thousand separator, it would be great if you took it from windows' locale settings.
 
This is one of the planned enhancements (see Future Improvements section). Hopefully will include it soon.
 
Regards,
Victor
 
phpWebNotes is a page annotation system modelled after php.net.
http://webnotes.sourceforge.net/demo.php[^]
GeneralRe: Nice, but...memberPaolo Messina1 Dec '02 - 11:55 
Another item for the "to do" list is the support for 64-bit file sizes. A single DWORD may not be enough for some huge files (such as AVI captures). I think a ULARGE_INTEGER or ULONGLONG should be fine, but I don't know which is supposed to be the standard fo file sizes, especially on a 64-bit Windows platform.
 
Paolo
 
------
"airplane is cool, but space shuttle is even better" (J. Kaczorowski)

GeneralRe: Nice, but...memberVictor Boctor1 Dec '02 - 15:25 
Sounds like a good idea. I will add this to the "To Do" list. Is there an advantage for ULARGE_INTEGER or ULONGLONG over __int64?
 
I will also add support for Terabyte.
 
Regards,
Victor
 
phpWebNotes is a page annotation system modelled after php.net.
http://webnotes.sourceforge.net/demo.php[^]
GeneralRe: Nice, but...memberDalle1 Dec '02 - 21:34 
ULARGE_INTEGER is a struct that works on compilers that does not support the 64-bit integers internally, where ULONGLONG is just a (Windows) typedef.
GeneralRe: Nice, but...memberPaolo Messina2 Dec '02 - 6:23 
Victor Boctor wrote:
I will also add support for Terabyte.
 
Yeah, and don't forget the others. See: http://www.tuxedo.org/~esr/jargon/html/entry/quantifiers.html[^]
 
Since you'll have 64bits you can reach an Exabyte!!! Big Grin | :-D
 
------
"airplane is cool, but space shuttle is even better" (J. Kaczorowski)

GeneralRe: Nice, but...memberVictor Boctor2 Dec '02 - 9:20 
Paolo Messina wrote:
Yeah, and don't forget the others. See: http://www.tuxedo.org/~esr/jargon/html/entry/quantifiers.html[^]
 
Thanks for the interesting link. I extracted the related part here:
 
kilo-   1000^1   1024^1 = 2^10 = 1,024 
mega-   1000^2   1024^2 = 2^20 = 1,048,576 
giga-   1000^3   1024^3 = 2^30 = 1,073,741,824 
tera-   1000^4   1024^4 = 2^40 = 1,099,511,627,776 
peta-   1000^5   1024^5 = 2^50 = 1,125,899,906,842,624 
exa-    1000^6   1024^6 = 2^60 = 1,152,921,504,606,846,976 
zetta-  1000^7   1024^7 = 2^70 = 1,180,591,620,717,411,303,424 
yotta-  1000^8   1024^8 = 2^80 = 1,208,925,819,614,629,174,706,176
The article will be updated to support Terabyta (TB), Petabyte (PB), and Exabyte (EB).
 
Thanks,
Victor
 
phpWebNotes is a page annotation system modelled after php.net.
http://webnotes.sourceforge.net/demo.php[^]
QuestionToo many numbers?memberDalle1 Dec '02 - 1:01 
I think there are too many numbers in some of the examples to make it easy to read. I would try to limit the precision to two value numbers.
 
IMHO if you have 1019-1075 bytes that should be presented as 1.0 kB, if you have 1009-1018 bytes that should be presented as 0.99 kB.
AnswerRe: Too many numbers?memberVictor Boctor1 Dec '02 - 2:18 
Dalle wrote:
IMHO if you have 1019-1075 bytes that should be presented as 1.0 kB, if you have 1009-1018 bytes that should be presented as 0.99 kB.
 
Can you provide the criteria/algorithm? Unsure | :~
 
I had a look at the Windows Explorer to get some ideas. I found that it does not display any decimal points, which looks good. However, it might not be accurate enough for some applications. Hence, I decided to add to the "Future Improvements", the support for an accuracy parameter which specifies the required number of decimal places.
 
Regards,
Victor.
 
phpWebNotes is a page annotation system modelled after php.net.
http://webnotes.sourceforge.net/demo.php[^]
GeneralRe: Too many numbers?memberKarstenK2 Dec '02 - 4:17 
If done this via division with 102. It is the next iteration for 1024/10Suspicious | :suss:
 
Try this @ home. (B&B)
GeneralRe: Too many numbers?memberCompMan442 Sep '08 - 12:35 
Victor Boctor wrote:
I had a look at the Windows Explorer to get some ideas. I found that it does not display any decimal points, which looks good.

 
Only in the "Size" column. If you take a look in the status bar, it shows exactly 3 significant digits for files 1KB or larger:
  • 1.07 GB
  • 460 MB
  • 34.0 KB
  • 1023 bytes
They're probably using some kind of black magic to do it, though. Poke tongue | ;-P
 
EDIT: Actually, I found it sometimes uses just 2 sigfig for sizes slightly below 1.00 _B, e.g. 999 KB is displayed as such, but 1000 KB is displayed "0.97 MB."
 

A little learning is a dangerous thing;
Drink deep, or taste not, the Pierian Spring.
     —Alexander Pope
modified on Tuesday, September 2, 2008 6:48 PM

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 1 Dec 2002
Article Copyright 2002 by Victor Boctor
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid