home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / mslang / format / format.cpp
Encoding:
Text File  |  1993-11-27  |  15.8 KB  |  341 lines

  1. /*******************************************************************************
  2.  *                                                                             *
  3.  * FILE:    FORMAT.CPP  Version 1.1                                            *
  4.  *                                                                             *
  5.  * PURPOSE: Format Class Definition.  Format is a general purpose class        *
  6.  *          to convert numbers to a string using a user defined pattern.       *
  7.  *                                                                             *
  8.  * Copyright (c) 1993, The Cantrell Group One, Inc.,                           *
  9.  * All rights reserved.                                                        *
  10.  *                                                                             *
  11.  * Authors: Gary Cantrell,   CIS ID:  73537,304                                *
  12.  *          Barry Childress, CIS ID:  73510,1420                               *
  13.  *                                                                             *
  14.  * DISCLAIMER:                                                                 *
  15.  *                                                                             *
  16.  * The authors do not make any representation or warranty, express or          *
  17.  * implied with respect to any code or other information herein.               *
  18.  * The authors disclaim any liability whatsoever for any use of such           *
  19.  * code or other information.                                                  *
  20.  *                                                                             *
  21.  *******************************************************************************
  22.  
  23. /* RCS stuff -- DO NOT REMOVE */
  24.  
  25. /* $Id: format.cpp,v 1.1 1993/11/27 20:08:13 GaryC Beta GaryC $
  26.  */
  27.  
  28. /* $Log: format.cpp,v $
  29.  * Revision 1.1  1993/11/27  20:08:13  GaryC
  30.  * Enhanced to provide more formats and capabilities.
  31.  *
  32.  * Revision 1.0  1993/10/27  12:00:00  Childress
  33.  * Initial version. Author:  Barry Childress, CIS ID: 73510,1420.
  34.  */
  35.  
  36. /*******************************************************************************
  37.  *                                                                             *
  38.  * Language:        Microsoft Visual C/C++ v1.1 for Windows NT;                *
  39.  *                  Also tested with MSVC v1.0 ( DOS & Windows 3.1 );          *
  40.  *                  Original version tested with Borland's C++ 3.1.            *
  41.  *                                                                             *                                                                             *
  42.  *******************************************************************************
  43.  *                                                                             *
  44.  * History: Version 1.0  10/27/93 - First "Beta" release ( Barry Childress )   *
  45.  *                                                                             *
  46.  *          Version 1.1  11/26/93 - Second "Beta" release                      *
  47.  *            - Enhanced to include more formats and capabilities.             *
  48.  *                                                                             *
  49.  *******************************************************************************
  50.  *                                                                             *
  51.  * The Format class allows you to format numbers using a pattern mask to       *
  52.  * define the output.  Each pattern mask can contain three sections that       *
  53.  * determine the format for positive numbers, negative numbers and zeroes.     *
  54.  * The sections are separated by semicolons.  If you include only one section  *
  55.  * then all numbers use that format.  If you include two sections then posi-   *
  56.  * tive numbers and zeroes use the first section and negative numbers use the  *
  57.  * second section.                                                             *
  58.  *                                                                             *
  59.  * The pattern mask uses special characters to determine the format            *
  60.  *                                                                             *
  61.  *      Format Symbol         Meaning                                          *
  62.  *      -------------         -----------------------------------------------  *
  63.  *                  0         Digit placeholder.  If the number has fewer      *
  64.  *                            digits on either side of the decimal point       *
  65.  *                            than there are zeros on either side of the       *
  66.  *                            decimal point in the pattern, Format displays    *
  67.  *                            the extra zeros.  If the number has more digits  *
  68.  *                            on the right side of the decimal point, Format   *
  69.  *                            rounds the number.  If there are more digits in  *
  70.  *                            the number to the left of the decimal point,     *
  71.  *                            Format displays the extra digits.                *
  72.  *                                                                             *
  73.  *                  #         Digit placeholder.  Follows the same rules as    *
  74.  *                            "0" except Format does not display the extra     *
  75.  *                            zeros if the number has fewer digits on either   *
  76.  *                            side of the decimal point.                       *
  77.  *                                                                             *
  78.  *                  ,(comma)  Thousands separator.  Format separates the       *
  79.  *                            thousands by commas if a pattern contains the    *
  80.  *                            commas surrounded by #'s or 0's.                 *
  81.  *                                                                             *
  82.  *                  .(period) Decimal point.  This symbol determines how many  *
  83.  *                            digits are displayed to the right and left of    *
  84.  *                            the decimal point.  If the pattern does not con- *
  85.  *                            tain a decimal point then Format rounds the      *
  86.  *                            number to a whole number.                        *
  87.  *                                                                             *
  88.  * The constructor is defined as follows:                                      *
  89.  *                                                                             *
  90.  *     Format::Format( const char *szPattern, int nWidth, double dNum ) ;      *
  91.  *                                                                             *
  92.  * Where:                                                                      *
  93.  *          szPattern   is a NULL terminated string containing the  pattern    *
  94.  *                      mask.                                                  *
  95.  *          nWidth      is the desired width of the returned string.           *
  96.  *          dNum        is the number to be displayed.                         *
  97.  *                                                                             *
  98.  * Example:                                                                    *
  99.  *                                                                             *
  100.  *            ...                                                              *
  101.  *                                                                             *
  102.  *            Format szValue( "#,##0.00", 15, 12345.675 ) ;                    *
  103.  *            cout << szValue << '\n' ;                                        *
  104.  *                                                                             *
  105.  * This code fragment would display the string, 12,345.68,                     *
  106.  * right justified in a field fifteen characters wide,                         *
  107.  *                                                                             *
  108.  * The following table shows several examples and the results:                 *
  109.  *                                                                             *
  110.  *                      Mask         Number         Result                     *
  111.  *      -------------------- -------------- --------------                     *
  112.  *                   #.#####            0.1             .1                     *
  113.  *                      #.00            0.1            .10                     *
  114.  *                       0.0            0.1            0.1                     *
  115.  *                      #.##          0.001              0                     *
  116.  *                      #.##       1234.568        1234.57                     *
  117.  *                      #.0#       12345.68       12345.68                     *
  118.  *                      #.0#             10           10.0                     *
  119.  *                     00000             12          00012                     *
  120.  *                  #,###.##        1234.50        1,234.5                     *
  121.  *                  #,##0.00        1234.50       1,234.50                     *
  122.  *                  #,##0.00     1234567.89   1,234,567.89                     *
  123.  *                     #,###     1234567.89      1,234,568                     *
  124.  *                      0.0%          21.35          21.4%                     *
  125.  *                      0.0%             21          21.0%                     *
  126.  *                        0%          21.55            22%                     *
  127.  *               ###-##-####      123456789    123-45-6789                     *
  128.  *                  #,##0.00         -22.45         -22.45                     *
  129.  *        #,##0.00;#,##0.00-         -22.45         22.45-                     *
  130.  *       #,##0.00;(#,##0.00)         -22.45        (22.45)                     *
  131.  *                $ #,##0.00     1234567.89 $ 1,234,567.89                     *
  132.  *                                                                             *
  133.  ******************************************************************************/
  134.  
  135. #define SHOW    // to include main() for testing
  136.  
  137. #include <stdio.h>
  138. #include <string.h>
  139. #include <iostream.h>
  140. #include <assert.h>
  141.  
  142. #ifndef BOOL
  143. typedef unsigned char BOOL ;
  144. #endif
  145.  
  146. #ifndef FALSE
  147. #undef  TRUE
  148. #define FALSE 0
  149. #define TRUE 1
  150. #endif
  151.  
  152. static const char IDC_PERIOD    =   '.' ;
  153. static const char IDC_COMMA     =   ',' ;
  154. static const char IDC_SEMICOLON =   ';' ;
  155. static const char IDC_POUND     =   '#' ;
  156. static const char IDC_ZERO      =   '0' ;
  157. static const char IDC_SPACE     =   ' ' ;
  158. static const int  IDC_MAXPAT    =   100 ;
  159.  
  160. class Format
  161. {
  162. public:
  163.     Format( const char *szPattern, int nFieldSize, double dNum ) ;
  164.     ~Format() ;
  165.  
  166.     // So you can use the Format class any where you would use a char*
  167.     operator const char*()  { return m_pszText ; }
  168.  
  169. private:
  170.     char *m_pszText ;
  171. } ;
  172.  
  173. Format::Format( const char *szPattern, int nFieldSize, double dNum )
  174. {
  175.     int nPrec = 0, nDec = 0 ;
  176.     assert( szPattern != NULL ) ;
  177.     assert( strlen( szPattern ) <= IDC_MAXPAT ) ;
  178.  
  179.     // create a non-const copy of szPattern
  180.     char szWork[IDC_MAXPAT + 1] ;
  181.     strcpy( szWork, szPattern ) ;
  182.  
  183.     char *apszPats[] = { NULL, NULL, NULL } ;
  184.  
  185.     // break out the masks
  186.     char *pszMask = apszPats[0] = strtok( szWork, ";" ) ;
  187.     for ( int i = 1; pszMask != NULL && i < 3; i++ )
  188.         pszMask = apszPats[i] = strtok( NULL, ";" ) ;
  189.  
  190.     // default mask to 1st pattern
  191.     pszMask = apszPats[0] ;
  192.  
  193.     if ( dNum < 0.0 && apszPats[1] != NULL )
  194.     {
  195.         pszMask = apszPats[1] ; // select negative mask
  196.         dNum = -dNum ;
  197.     }
  198.     else if ( dNum == 0.0 && apszPats[2] != NULL )
  199.         pszMask = apszPats[2] ; // select positive mask
  200.  
  201.     assert(pszMask != NULL ) ;
  202.  
  203.     // find precision for sprintf()
  204.     const char *pszPat = strchr ( pszMask, IDC_PERIOD ) ;
  205.  
  206.     if ( pszPat )
  207.     {
  208.         while ( *++pszPat )
  209.             if ( *pszPat == IDC_POUND || *pszPat == IDC_ZERO )
  210.                 nPrec++ ;
  211.     }
  212.  
  213.     // mark the first placeholder in the pattern
  214.     int nMask = strcspn( pszMask, "#0" ) ;
  215.  
  216.     m_pszText = new char[nFieldSize + 1] ;
  217.  
  218.     // converted number into the destination field
  219.     sprintf ( m_pszText, "%-*.*lf", nFieldSize, nPrec, dNum ) ;
  220.  
  221.     // the constructed number will be shifted in on the extreme right
  222.     // of the destination field ( from right to left ).
  223.     char *pszDest = m_pszText + nFieldSize ;
  224.  
  225.     // find the end of the string sprintf() created
  226.     char *pszSrc = strchr ( m_pszText, IDC_SPACE ) ;
  227.  
  228.     // if trailing spaces, back up to last digit or end of field
  229.     pszSrc = pszSrc ? pszSrc -1 : m_pszText + strlen ( m_pszText ) - 1 ;
  230.  
  231.     BOOL bInFrac = nPrec > 0 ;
  232.     BOOL bHasCommas = FALSE ;
  233.     BOOL bSig = FALSE ;
  234.  
  235.     // point to the right side of the pattern
  236.     pszPat = pszMask + strlen ( pszMask ) ;
  237.  
  238.     // shift in format characters
  239.     while ( pszSrc >= m_pszText )
  240.     {
  241.         switch ( *pszPat )
  242.         {
  243.             case IDC_ZERO:
  244.             case IDC_POUND:
  245.                 if ( *pszPat == IDC_ZERO )
  246.                 {
  247.                     *pszDest-- = *pszSrc-- ;
  248.                     bSig = TRUE ;
  249.                 }
  250.                 else
  251.                     if ( bInFrac )
  252.                         if ( bSig || *pszSrc != IDC_ZERO )
  253.                         {
  254.                             *pszDest-- = *pszSrc-- ;
  255.                             bSig = TRUE ;
  256.                         }
  257.                         else
  258.                             --pszSrc ;
  259.                     else
  260.                         if ( bSig && dNum > 0.0 && dNum < 1.0 )
  261.                             --pszSrc ;
  262.                         else
  263.                             *pszDest-- = *pszSrc-- ;
  264.                 break ;
  265.  
  266.             case IDC_PERIOD:
  267.                 assert( *pszSrc == IDC_PERIOD ) ;
  268.                 if ( bSig )
  269.                     *pszDest-- = *pszSrc ;
  270.                 --pszSrc ;
  271.                 bInFrac = FALSE ;   // signal end of fractional part
  272.                 break ;
  273.  
  274.             default:
  275.                 *pszDest-- = *pszPat ;  // shift in pattern literals
  276.                 if ( *pszPat == IDC_COMMA )
  277.                     bHasCommas = TRUE ;
  278.                 break ;
  279.         }
  280.  
  281.         --pszPat ;
  282.  
  283.         // we need to extend the pattern until we exhaust the source
  284.         if ( pszPat < pszMask + nMask && pszSrc >= m_pszText )
  285.             pszPat = bHasCommas ? pszMask + nMask + 3 : pszMask + nMask ;
  286.     }
  287.  
  288.     // shift in any remaining mask literals
  289.     for ( ; pszPat >= pszMask && pszDest >= m_pszText; --pszPat )
  290.         if ( *pszPat != IDC_POUND && *pszPat != IDC_COMMA )
  291.             *pszDest-- = *pszPat ;
  292.  
  293.     // blank unused destinition
  294.     for ( ; pszDest >= m_pszText; --pszDest )
  295.         *pszDest = IDC_SPACE ;
  296. }
  297.  
  298. Format::~Format()
  299. {
  300.     delete [] m_pszText ;
  301. }
  302.  
  303. #ifdef SHOW
  304.  
  305. #define show(pat,num) cout.width(20); cout << pat; cout.width(15); \
  306.                       cout << #num  << Format( pat, 15, ##num ) << endl
  307.  
  308. void main()
  309. {
  310.     cout << endl ;
  311.     cout.width(20) ;
  312.     cout << "Mask" ;
  313.     cout.width(15) ;
  314.     cout << "Number" ;
  315.     cout.width(15) ;
  316.     cout << "Result" << endl ;
  317.     cout << "--------------------------------------------------" << endl ;
  318.  
  319.     show( "#.#####", 0.1 ) ;
  320.     show( "#.00", 0.1 ) ;
  321.     show( "0.0", 0.1 ) ;
  322.     show( "#.##", .001 ) ;
  323.     show( "#.##", 1234.568 ) ;
  324.     show( "#.0#", 12345.68 ) ;
  325.     show( "#.0#", 10 ) ;
  326.     show( "00000", 12 ) ;
  327.     show( "#,###.##", 1234.50 ) ;
  328.     show( "#,##0.00", 1234.50 ) ;
  329.     show( "#,##0.00", 1234567.89 ) ;
  330.     show( "#,###", 1234567.89 ) ;
  331.     show( "0.0%", 21.35 ) ;
  332.     show( "0.0%", 21 ) ;
  333.     show( "0%", 21.55 ) ;
  334.     show( "###-##-####", 123456789 ) ;
  335.     show( "#,##0.00", -22.45 ) ;
  336.     show( "#,##0.00;#,##0.00-", -22.45 ) ;
  337.     show( "#,##0.00;(#,##0.00)", -22.45 ) ;
  338.     show( "$ #,##0.00", 1234567.89 ) ;
  339. }
  340. #endif
  341.