Click here to Skip to main content
Email Password   helpLost your password?

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

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:

History

01-DEC-2002First version

Edit History

2 Dec 2002 - Initial Edit

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralGeneralize Solution
Mina Victor
15:14 4 Jan '10  
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
GeneralEmbroidery
mmhafez
7:54 17 Sep '03  
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

Eng. Mohamed M. Hafez
GeneralAlso see GetNumberFormat()
Ravi Bhavnani
7:35 1 Dec '02  
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 way
dabs
3:16 1 Dec '02  
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 way
Victor Boctor
3:31 1 Dec '02  
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

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 way
Andreas Saurwein
1:24 4 Dec '02  
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...
Anders Molin
2:02 1 Dec '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...
Victor Boctor
2:11 1 Dec '02  
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...
Paolo Messina
12:55 1 Dec '02  
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...
Victor Boctor
16:25 1 Dec '02  
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...
Dalle
22:34 1 Dec '02  
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...
Paolo Messina
7:23 2 Dec '02  
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

------
"airplane is cool, but space shuttle is even better" (J. Kaczorowski)

GeneralRe: Nice, but...
Victor Boctor
10:20 2 Dec '02  
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[^]
GeneralToo many numbers?
Dalle
2:01 1 Dec '02  
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.
GeneralRe: Too many numbers?
Victor Boctor
3:18 1 Dec '02  
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?
KarstenK
5:17 2 Dec '02  
If done this via division with 102. It is the next iteration for 1024/10Suspicious

Try this @ home. (B&B)
GeneralRe: Too many numbers?
CompMan44
13:35 2 Sep '08  
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

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


Last Updated 1 Dec 2002 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010