home *** CD-ROM | disk | FTP | other *** search
- /*******************************************************************************
- * *
- * FILE: FORMAT.CPP Version 1.1 *
- * *
- * PURPOSE: Format Class Definition. Format is a general purpose class *
- * to convert numbers to a string using a user defined pattern. *
- * *
- * Copyright (c) 1993, The Cantrell Group One, Inc., *
- * All rights reserved. *
- * *
- * Authors: Gary Cantrell, CIS ID: 73537,304 *
- * Barry Childress, CIS ID: 73510,1420 *
- * *
- * DISCLAIMER: *
- * *
- * The authors do not make any representation or warranty, express or *
- * implied with respect to any code or other information herein. *
- * The authors disclaim any liability whatsoever for any use of such *
- * code or other information. *
- * *
- *******************************************************************************
-
- /* RCS stuff -- DO NOT REMOVE */
-
- /* $Id: format.cpp,v 1.1 1993/11/27 20:08:13 GaryC Beta GaryC $
- */
-
- /* $Log: format.cpp,v $
- * Revision 1.1 1993/11/27 20:08:13 GaryC
- * Enhanced to provide more formats and capabilities.
- *
- * Revision 1.0 1993/10/27 12:00:00 Childress
- * Initial version. Author: Barry Childress, CIS ID: 73510,1420.
- */
-
- /*******************************************************************************
- * *
- * Language: Microsoft Visual C/C++ v1.1 for Windows NT; *
- * Also tested with MSVC v1.0 ( DOS & Windows 3.1 ); *
- * Original version tested with Borland's C++ 3.1. *
- * * *
- *******************************************************************************
- * *
- * History: Version 1.0 10/27/93 - First "Beta" release ( Barry Childress ) *
- * *
- * Version 1.1 11/26/93 - Second "Beta" release *
- * - Enhanced to include more formats and capabilities. *
- * *
- *******************************************************************************
- * *
- * The Format class allows you to format numbers using a pattern mask to *
- * define the output. Each pattern mask can contain three sections that *
- * determine the format for positive numbers, negative numbers and zeroes. *
- * The sections are separated by semicolons. If you include only one section *
- * then all numbers use that format. If you include two sections then posi- *
- * tive numbers and zeroes use the first section and negative numbers use the *
- * second section. *
- * *
- * The pattern mask uses special characters to determine the format *
- * *
- * Format Symbol Meaning *
- * ------------- ----------------------------------------------- *
- * 0 Digit placeholder. If the number has fewer *
- * digits on either side of the decimal point *
- * than there are zeros on either side of the *
- * decimal point in the pattern, Format displays *
- * the extra zeros. If the number has more digits *
- * on the right side of the decimal point, Format *
- * rounds the number. If there are more digits in *
- * the number to the left of the decimal point, *
- * Format displays the extra digits. *
- * *
- * # Digit placeholder. Follows the same rules as *
- * "0" except Format does not display the extra *
- * zeros if the number has fewer digits on either *
- * side of the decimal point. *
- * *
- * ,(comma) Thousands separator. Format separates the *
- * thousands by commas if a pattern contains the *
- * commas surrounded by #'s or 0's. *
- * *
- * .(period) Decimal point. This symbol determines how many *
- * digits are displayed to the right and left of *
- * the decimal point. If the pattern does not con- *
- * tain a decimal point then Format rounds the *
- * number to a whole number. *
- * *
- * The constructor is defined as follows: *
- * *
- * Format::Format( const char *szPattern, int nWidth, double dNum ) ; *
- * *
- * Where: *
- * szPattern is a NULL terminated string containing the pattern *
- * mask. *
- * nWidth is the desired width of the returned string. *
- * dNum is the number to be displayed. *
- * *
- * Example: *
- * *
- * ... *
- * *
- * Format szValue( "#,##0.00", 15, 12345.675 ) ; *
- * cout << szValue << '\n' ; *
- * *
- * This code fragment would display the string, 12,345.68, *
- * right justified in a field fifteen characters wide, *
- * *
- * The following table shows several examples and the results: *
- * *
- * Mask Number Result *
- * -------------------- -------------- -------------- *
- * #.##### 0.1 .1 *
- * #.00 0.1 .10 *
- * 0.0 0.1 0.1 *
- * #.## 0.001 0 *
- * #.## 1234.568 1234.57 *
- * #.0# 12345.68 12345.68 *
- * #.0# 10 10.0 *
- * 00000 12 00012 *
- * #,###.## 1234.50 1,234.5 *
- * #,##0.00 1234.50 1,234.50 *
- * #,##0.00 1234567.89 1,234,567.89 *
- * #,### 1234567.89 1,234,568 *
- * 0.0% 21.35 21.4% *
- * 0.0% 21 21.0% *
- * 0% 21.55 22% *
- * ###-##-#### 123456789 123-45-6789 *
- * #,##0.00 -22.45 -22.45 *
- * #,##0.00;#,##0.00- -22.45 22.45- *
- * #,##0.00;(#,##0.00) -22.45 (22.45) *
- * $ #,##0.00 1234567.89 $ 1,234,567.89 *
- * *
- ******************************************************************************/
-
- #define SHOW // to include main() for testing
-
- #include <stdio.h>
- #include <string.h>
- #include <iostream.h>
- #include <assert.h>
-
- #ifndef BOOL
- typedef unsigned char BOOL ;
- #endif
-
- #ifndef FALSE
- #undef TRUE
- #define FALSE 0
- #define TRUE 1
- #endif
-
- static const char IDC_PERIOD = '.' ;
- static const char IDC_COMMA = ',' ;
- static const char IDC_SEMICOLON = ';' ;
- static const char IDC_POUND = '#' ;
- static const char IDC_ZERO = '0' ;
- static const char IDC_SPACE = ' ' ;
- static const int IDC_MAXPAT = 100 ;
-
- class Format
- {
- public:
- Format( const char *szPattern, int nFieldSize, double dNum ) ;
- ~Format() ;
-
- // So you can use the Format class any where you would use a char*
- operator const char*() { return m_pszText ; }
-
- private:
- char *m_pszText ;
- } ;
-
- Format::Format( const char *szPattern, int nFieldSize, double dNum )
- {
- int nPrec = 0, nDec = 0 ;
- assert( szPattern != NULL ) ;
- assert( strlen( szPattern ) <= IDC_MAXPAT ) ;
-
- // create a non-const copy of szPattern
- char szWork[IDC_MAXPAT + 1] ;
- strcpy( szWork, szPattern ) ;
-
- char *apszPats[] = { NULL, NULL, NULL } ;
-
- // break out the masks
- char *pszMask = apszPats[0] = strtok( szWork, ";" ) ;
- for ( int i = 1; pszMask != NULL && i < 3; i++ )
- pszMask = apszPats[i] = strtok( NULL, ";" ) ;
-
- // default mask to 1st pattern
- pszMask = apszPats[0] ;
-
- if ( dNum < 0.0 && apszPats[1] != NULL )
- {
- pszMask = apszPats[1] ; // select negative mask
- dNum = -dNum ;
- }
- else if ( dNum == 0.0 && apszPats[2] != NULL )
- pszMask = apszPats[2] ; // select positive mask
-
- assert(pszMask != NULL ) ;
-
- // find precision for sprintf()
- const char *pszPat = strchr ( pszMask, IDC_PERIOD ) ;
-
- if ( pszPat )
- {
- while ( *++pszPat )
- if ( *pszPat == IDC_POUND || *pszPat == IDC_ZERO )
- nPrec++ ;
- }
-
- // mark the first placeholder in the pattern
- int nMask = strcspn( pszMask, "#0" ) ;
-
- m_pszText = new char[nFieldSize + 1] ;
-
- // converted number into the destination field
- sprintf ( m_pszText, "%-*.*lf", nFieldSize, nPrec, dNum ) ;
-
- // the constructed number will be shifted in on the extreme right
- // of the destination field ( from right to left ).
- char *pszDest = m_pszText + nFieldSize ;
-
- // find the end of the string sprintf() created
- char *pszSrc = strchr ( m_pszText, IDC_SPACE ) ;
-
- // if trailing spaces, back up to last digit or end of field
- pszSrc = pszSrc ? pszSrc -1 : m_pszText + strlen ( m_pszText ) - 1 ;
-
- BOOL bInFrac = nPrec > 0 ;
- BOOL bHasCommas = FALSE ;
- BOOL bSig = FALSE ;
-
- // point to the right side of the pattern
- pszPat = pszMask + strlen ( pszMask ) ;
-
- // shift in format characters
- while ( pszSrc >= m_pszText )
- {
- switch ( *pszPat )
- {
- case IDC_ZERO:
- case IDC_POUND:
- if ( *pszPat == IDC_ZERO )
- {
- *pszDest-- = *pszSrc-- ;
- bSig = TRUE ;
- }
- else
- if ( bInFrac )
- if ( bSig || *pszSrc != IDC_ZERO )
- {
- *pszDest-- = *pszSrc-- ;
- bSig = TRUE ;
- }
- else
- --pszSrc ;
- else
- if ( bSig && dNum > 0.0 && dNum < 1.0 )
- --pszSrc ;
- else
- *pszDest-- = *pszSrc-- ;
- break ;
-
- case IDC_PERIOD:
- assert( *pszSrc == IDC_PERIOD ) ;
- if ( bSig )
- *pszDest-- = *pszSrc ;
- --pszSrc ;
- bInFrac = FALSE ; // signal end of fractional part
- break ;
-
- default:
- *pszDest-- = *pszPat ; // shift in pattern literals
- if ( *pszPat == IDC_COMMA )
- bHasCommas = TRUE ;
- break ;
- }
-
- --pszPat ;
-
- // we need to extend the pattern until we exhaust the source
- if ( pszPat < pszMask + nMask && pszSrc >= m_pszText )
- pszPat = bHasCommas ? pszMask + nMask + 3 : pszMask + nMask ;
- }
-
- // shift in any remaining mask literals
- for ( ; pszPat >= pszMask && pszDest >= m_pszText; --pszPat )
- if ( *pszPat != IDC_POUND && *pszPat != IDC_COMMA )
- *pszDest-- = *pszPat ;
-
- // blank unused destinition
- for ( ; pszDest >= m_pszText; --pszDest )
- *pszDest = IDC_SPACE ;
- }
-
- Format::~Format()
- {
- delete [] m_pszText ;
- }
-
- #ifdef SHOW
-
- #define show(pat,num) cout.width(20); cout << pat; cout.width(15); \
- cout << #num << Format( pat, 15, ##num ) << endl
-
- void main()
- {
- cout << endl ;
- cout.width(20) ;
- cout << "Mask" ;
- cout.width(15) ;
- cout << "Number" ;
- cout.width(15) ;
- cout << "Result" << endl ;
- cout << "--------------------------------------------------" << endl ;
-
- show( "#.#####", 0.1 ) ;
- show( "#.00", 0.1 ) ;
- show( "0.0", 0.1 ) ;
- show( "#.##", .001 ) ;
- show( "#.##", 1234.568 ) ;
- show( "#.0#", 12345.68 ) ;
- show( "#.0#", 10 ) ;
- show( "00000", 12 ) ;
- show( "#,###.##", 1234.50 ) ;
- show( "#,##0.00", 1234.50 ) ;
- show( "#,##0.00", 1234567.89 ) ;
- show( "#,###", 1234567.89 ) ;
- show( "0.0%", 21.35 ) ;
- show( "0.0%", 21 ) ;
- show( "0%", 21.55 ) ;
- show( "###-##-####", 123456789 ) ;
- show( "#,##0.00", -22.45 ) ;
- show( "#,##0.00;#,##0.00-", -22.45 ) ;
- show( "#,##0.00;(#,##0.00)", -22.45 ) ;
- show( "$ #,##0.00", 1234567.89 ) ;
- }
- #endif
-