How to read Unicode text files

16 04 2013

Recently I met with a problem when I wrote some codes to read a file using MFC API CStdioFile. But unfortunately it failed to read UNICODE files :(. Then only I came to know that the MFC classes CFile and CStdioFile can read/write text files only in ANSI format. So how we will resolve it?

Windows provides another API fopen_s for Unicode file streams. This API supports encoding flag, where we cab mention the desired flag while reading a file or writing a file.

fopen_s(&fStream, “UnicodeFile.txt”, “r,ccs=encoding”);

    
BOOL ReadUnicodeFile()
{
    CString strFileName = _T("C:\\UnicodeFile.txt");
    FILE *fStream;
    errno_t errCode = _tfopen_s(&fStream, strFileName, _T("r, ccs=UNICODE"));
    if (0 != errCode)
    {
        return FALSE;
    }
    CStdioFile File(fStream);
    CString strLine;
    while(File.ReadString(strLine))
    {
        //reading line-by-line
    }
}

ccs=ENCODING
Specify the coded character set to use (ANSI, UTF-8, UTF-16LE, and UNICODE) for this file. This option is available in Visual C++ 2005 and later.

Advertisements




How to convert time in seconds to Years, Months, Weeks, Days, Hours etc…

29 12 2012

There are several different formats used in different places to represent time. Recently I met with a problem that in one of my applications, it was getting time in seconds and the requirement was to calculate how many Years, Moths, Weeks, Days, Hours, Minutes and Seconds in that.

A small algorithm with division (/) and modulo (%) operator makes us to do it easily.

const int DAYS_IN_YEAR = 365;
const int DAYS_IN_MONTH = 30; //consider 30 Days
const int DAYS_IN_WEEK = 7;
const int HOURS_IN_DAY = 24;
const int MINS_IN_HOUR = 60;
const int SECS_IN_MIN = 60;

CString ConvertTime(long nInputInSeconds)
{
	int nYears   = 0;
	int nMonths  = 0;
	int nWeeks   = 0;
	int nDays    = 0;
	int nHours   = 0;
	int nMinutes = 0;
	int nSeconds = 0;

	const int nSecondsInOneYear = SECS_IN_MIN * MINS_IN_HOUR * HOURS_IN_DAY * DAYS_IN_YEAR;
	const int nSecondsInOneMonth = SECS_IN_MIN * MINS_IN_HOUR * HOURS_IN_DAY * DAYS_IN_MONTH;
	const int nSecondsInOneWeek = SECS_IN_MIN * MINS_IN_HOUR * HOURS_IN_DAY * DAYS_IN_WEEK;
	const int nSecondsInOneDay = SECS_IN_MIN * MINS_IN_HOUR * HOURS_IN_DAY;
	const int nSecondsInOneHour = SECS_IN_MIN * MINS_IN_HOUR;

	nYears = nInputInSeconds / nSecondsInOneYear;
	nMonths = (nInputInSeconds % nSecondsInOneYear) / nSecondsInOneMonth;
	nWeeks = ((nInputInSeconds % nSecondsInOneYear) % nSecondsInOneMonth ) / nSecondsInOneWeek;
	nDays = (((nInputInSeconds % nSecondsInOneYear) % nSecondsInOneMonth ) % nSecondsInOneWeek) / nSecondsInOneDay;
	nHours = ((((nInputInSeconds % nSecondsInOneYear) % nSecondsInOneMonth ) % nSecondsInOneWeek) % nSecondsInOneDay) / nSecondsInOneHour;
	nMinutes = (((((nInputInSeconds % nSecondsInOneYear) % nSecondsInOneMonth ) % nSecondsInOneWeek) % nSecondsInOneDay) % nSecondsInOneHour) / SECS_IN_MIN;
	nSeconds = ((((((nInputInSeconds % nSecondsInOneYear) % nSecondsInOneMonth ) % nSecondsInOneWeek) % nSecondsInOneDay) % nSecondsInOneHour) % SECS_IN_MIN);

	CString str;
	str.Format(_T("%d Years, %d Months, %d Weeks, %d Days, %d Hours, %d Minutes, %d Seconds"),
		nYears, nMonths, nWeeks, nDays, nHours, nMinutes, nSeconds);
	return str;
}

You may get some minor differences as it always considers exactly 30 days in each months.





How to format string

7 10 2012

Formatting a string is one of the most commonly used methods in our daily programming. But inappropriate usage of format specifiers may lead to unexpected crash. This post summarizes the format specifiers supported by CString Format method.

MFC class CString provides a Format method to format the string. String replaceable parameters (%s, %d, %c etc) can be used to format the string.

void Format(LPCTSTR lpszFormat, … );
LPCTSTR lpszFormat – format specifier

Format Specifiers

Sr.No. Specifier Description
1 String %s
2 Character %c
3 Integer %d or %i
4 Unsigned Integer %u
5 Float and double %f
6 ULONGLONG %I64u
7 SIZE_T and DWORD %lu
8 Format float with 3 decimal places %.3f
9 Format float with 3 decimal places, right justified with 5 characters %5.3f
10 Format float with 3 decimal places, left justified with 5 characters %-5.3f
11 Format int, left justified 5 characters %-5d
12 Format int, right justified 5 characters, use 0 to fill the field %05d
13 void pointer %p
14 Output a % sign %%

void FormatString()
{
	CString str;
	str.Format(_T("%s"), _T("Hello world!")); //Hello world!
	str.Format(_T("Integer - %d"), 123); //Integer - 123
	char temp1 = 'a';
	str.Format(_T("Character - %c"), temp1); //Character - a
	float temp2 = 1.234;
	str.Format(_T("Folat - %f"), temp2); //Folat - 1.234
	ULONGLONG temp3 = 123456789;
	str.Format(_T("ULONGLONG - %I64u"), temp3); //ULONGLONG - 123456789
	SIZE_T temp4 = 12344;
	str.Format(_T("SIZE_T - %lu"), temp4);//SIZE_T - 12344
	float temp5 = 1.234567;
	str.Format(_T("Float 3 decimal places - %.3f"), temp5);//Float 3 decimal places - 1.235
	str.Format(_T("Float 3 decimal places, right justified 5 chars - %5.3f"), temp5);//Float 3 decimal places, right justified 5 chars - 00001.234
	str.Format(_T("Float 3 decimal places, left justified 5 chars - %-5.3f"), temp5);//Float 3 decimal places, left justified 5 chars - 1.235
	int temp6 = 12;
	str.Format(_T("Int Left aligned 5 character - %.5d"), temp6);//Int Left aligned 5 character - 00012
	str.Format(_T("Int Right aligned 5 character, fill 0 - %05d"), temp6);//Int Right aligned 5 character, fill 0 - 00123
}

It is better to avoid string object itself as the parameter to Format method.





How to get and set Date and Time

11 02 2012

Sometimes we may really need to set/get date and time values to/from system. So whenever we think about to change these parameters generally we should think about time zones and daylight savings time, because most of the time our applications need to run globally in a safe manner.

The time related functions can return time in different formats. The below two are the commonly used formats.

1) System timeUTC based time (Coordinated Universal Time)
2) Local Time – Time based on local time zone

Win32 API provides different functions to set/get System Time and Local Time separately.
GetSystemTime/SetSystemTime – To set or retrieve current system date and time. This is expressed in UTC.
GetLocalTime/SetLocalTime – To set or retrieve current local date and time.

These all functions are using a pointer to a SYSTEMTIME structure as a parameter to receive or contains the current or new system date and time respectively.

void GetTime()
{
	SYSTEMTIME st, lt;

	GetSystemTime(&st);
	GetLocalTime(<);

	CString strSystemTime, strLocalTime;

	strSystemTime.Format( _T("Year = %04d Month = %02d Day = %02d Hour = %02d Min = %02d"),
		st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute);

	strLocalTime.Format( _T("Year = %04d Month = %02d Day = %02d Hour = %02d Min = %02d"),
		lt.wYear, lt.wMonth, lt.wDay, lt.wHour, lt.wMinute);

	AfxMessageBox(_T("SystemTime : " + strSystemTime + "\n") + "LocalTime : " + strLocalTime);
}

The MFC framework simplified the working with times by introducing two wrapper classes, the CTime and COleDatetime classes.





How to read/write a file

11 02 2012

Reading the contents of a file or writing to a file is common in any kind of applications. So how to open and close files as well as read and write to those files.

MFC provides a class CStdioFile to perform read/write operations from/to a file. This is a CFile derived class and the base class function Open would open an existing file or create a new file as per the flag uses in the second parameter of this function.
CStdioFile has two separate functions ReadString and WriteString to perform read and write operations respectively. Each functions read/write a single line of text.

  

//Create a new file

void WriteToFile()
{
	CStdioFile writeToFile;
	CFileException fileException;
	CString strFilePath = _T("C:\\Test.txt");

	if (writeToFile.Open( strFilePath, CFile::modeCreate|CFile::modeWrite), &fileException)
	{
		writeToFile.WriteString(_T("Hello\n"));
		writeToFile.WriteString(_T("Hi\n"));
		writeToFile.WriteString(_T("Bye\n"));
	}
	else
	{
		CString strErrorMsg;
		strErrorMsg.Format(_T("Can't open file %s , error : %u"), strFilePath, fileException.m_cause);
		AfxMessageBox(strErrorMsg);
	}

	writeToFile.Close();
}

//Read the contents of a file

void ReadFile()
{
	CStdioFile readFile;
	CFileException fileException;
	CString strFilePath = _T("C:\\Test.txt");
	CString strLine;

	if (readFile.Open(strFilePath, CFile::modeRead, &fileException))
	{
		while (readFile.ReadString(strLine));
	}
	else
	{
		CString strErrorMsg;
		strErrorMsg.Format(_T("Can't open file %s , error : %u"), strFilePath, fileException.m_cause);
		AfxMessageBox(strErrorMsg);
	}

	readFile.Close();
}

CFile is the base class for MFC file classes.It directly supports memory files and text files streaming through its derived classes.





How to change the default font of a control

22 01 2012

Each windows controls(Static Text, Edit, Button etc) have its own default fonts. Sometimes we may need to change this for a better visibility or some other usability requirements.

Before setting a font to a control, we have to create a CFont object with desired size and other parameters using CreateFont function. Then we can set that font to any controls using CWnd’s SetFont function.

CFont newFont;

void ChangeFont()
{
	/** Create a font*/

    newFont.CreateFont(20,
		20,
		0,
		0,
		FW_BOLD,
		FALSE,
		FALSE,
		0,
		DEFAULT_CHARSET,
		OUT_CHARACTER_PRECIS,
		CLIP_CHARACTER_PRECIS,
		DEFAULT_QUALITY,
		DEFAULT_PITCH,
		NULL);

	/** Get the handle of a control*/
	CWnd* pCtrlWnd = GetDlgItem(IDC_ST_TEST_CONTROL);

	/** Set new font for that control*/
	pCtrlWnd->SetFont(&newFont, TRUE);
}

Font can be created in many ways, but the only important thing is font object should exist as long as the control exist. Otherwise font object should decalre as a member or a global object.





How to read an Excel file

22 01 2012

The previous post was about to read an xml file. Just like that, Excel(xls) files are another commonly used format to store data in our applications. In this post we will see how to read an excel file.

There are mainly two methods to read an excel file.

1) Excel Viewer ActiveX control

2) ODBC method

Using Microsoft office ActiveX control is the direct and easiest method, but it has some system dependencies  to load the file. So its better to choose ODBC method.  ODBC reading using two MFC classes CDatabase and CRecordset to read the file.  Please see the following example to get a clear idea.

See below sample code to read the content of CountryCapitalInfo.xls file.

ReadExcel.h

#pragma once

#include 

class CReadExcel
{
public: /** Public constructor/destructor */

    /** Constructor. */
    CReadExcel(void); 

    /** Destructor. */
    ~CReadExcel(void);

private: /** Prevent object cloning/copying */

    /** Copy constructor. */
    CReadExcel(const CReadExcel&);  

    /** Assignment operator overloaded */
    CReadExcel& operator=(const CReadExcel&);

public:

    /** Read the given xls file and fill the file data to an arrary.
    Table name must be Product_Info*/
    BOOL ReadExcelFile(/*[in]*/ CString& p_strFilePath,
                       /*[out]*/ std::vector& p_arrProductDetails );

private:

    /** Get the name of the Excel-ODBC driver */
    CString GetExcelDriver();
};

ReadExcel.cpp

#include "StdAfx.h"
#include "ReadExcel.h"
#include "odbcinst.h"
#include "afxdb.h"

CReadExcel::CReadExcel(void)
{
}

CReadExcel::~CReadExcel(void)
{
}

BOOL CReadExcel::ReadExcelFile(/*[in]*/ CString& p_strFilePath,
                              /*[out]*/ std::vector& p_arrProductDetails)
{
    CDatabase   database;
    CString     strDriver;
    CString     strDsn;	

    // Retrieve the name of the Excel driver.
    strDriver = GetExcelDriver();
    if (strDriver.IsEmpty())
    {
        AfxMessageBox(_T("No excel ODBC driver."));
        return FALSE;
    }

    strDsn.Format(_T("ODBC;DRIVER={%s};DSN='';DBQ=%s"), strDriver, p_strFilePath);

    try
    {
        // Open the database
        database.Open(NULL,false,false,strDsn);

        CRecordset recset( &database );

        CString strSql;

        // Build the SQL string
        strSql.Format(_T("SELECT * FROM %s"), _T("MyTable"));

        recset.Open(CRecordset::forwardOnly, strSql, CRecordset::readOnly);

        unsigned long count = recset.GetRecordCount() ; 

        CString strProductInfo;
        strProductInfo.Format(_T("%s%s%s"), _T("Country"), _T("-"), _T("Capital"));
        p_arrProductDetails.push_back(strProductInfo);

        CString str1, str2;
        CString strItem1, strItem2;
        while( !recset.IsEOF() )
        {
            str1.Empty();
            str2.Empty();

            // Read the result line
            recset.GetFieldValue(_T("Country"), strItem1);
            str1 += strItem1;    

            recset.GetFieldValue(_T("Capital"), strItem2);
            str2 += strItem2;

            strProductInfo.Format(_T("%s%s%s"), str1, _T("-"), str2);
            p_arrProductDetails.push_back(strProductInfo);

            // Skip to the next resultline
            recset.MoveNext();
        }
        // Close the database
        database.Close();

    }
    catch(CDBException e)
    {
        AfxMessageBox(_T("Database error: ") + e.m_strError);
        return FALSE;
    }
    catch(...)
    {
        AfxMessageBox(_T("Could not open  the excel file."));
        return FALSE;
    }

    return TRUE;
}

CString CReadExcel::GetExcelDriver()
{
    char szBuf[2001];
    WORD cbBufMax = 2000;
    WORD cbBufOut;
    char *pszBuf = szBuf;
    CString strDriver;

    // Get the names of the installed drivers
    if (!SQLGetInstalledDrivers(szBuf, cbBufMax,&cbBufOut))
        return "";

    // Search for the driver...
    do
    {
        if( strstr( pszBuf, _T("Excel") ) != 0 )
        {
            // Found !
            strDriver = CString( pszBuf );
            break;
        }
        pszBuf = strchr( pszBuf, _T('') ) + 1;
    }
    while (pszBuf[1] != _T(''));

    return strDriver;
}

TestCode

BOOL ReadExcelFile(CString p_strFilePath)
{
 CWaitCursor wait;
 CReadExcel readExcel;
 std::vector<CString> arrExcelInfo;
 if (!readExcel.ReadExcelFile(p_strFilePath, arrExcelInfo))
 {
  return FALSE;
 }
 return TRUE;
}

 

Don’t forget to include the header files odbcinst.h and afxdb.h to your project.
One of the drawbacks of this method is that you have to set a name for the data section (Insert->Names) in the excel sheet.