StdioFileEx.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. // StdioFileEx.cpp: implementation of the CStdioFileEx class.
  2. //
  3. // Version 1.1 23 August 2003. Incorporated fixes from Dennis Jeryd.
  4. // Version 1.3 19 February 2005. Incorporated fixes Howard J Oh and some of my own.
  5. //
  6. // Copyright David Pritchard 2003-2005. [email protected]
  7. //
  8. // You can use this class freely, but please keep my ego happy
  9. // by leaving this comment in place.
  10. //
  11. //////////////////////////////////////////////////////////////////////
  12. #include "stdafx.h"
  13. #include "StdioFileEx.h"
  14. #ifdef _DEBUG
  15. #undef THIS_FILE
  16. static char THIS_FILE[]=__FILE__;
  17. #define new DEBUG_NEW
  18. #endif
  19. //////////////////////////////////////////////////////////////////////
  20. // Construction/Destruction
  21. //////////////////////////////////////////////////////////////////////
  22. /*static*/ const UINT CStdioFileEx::modeWriteUnicode = 0x20000; // Add this flag to write in Unicode
  23. CStdioFileEx::CStdioFileEx(): CStdioFile()
  24. {
  25. m_bIsUnicodeText = false;
  26. }
  27. CStdioFileEx::CStdioFileEx(LPCTSTR lpszFileName,UINT nOpenFlags)
  28. :CStdioFile(lpszFileName, ProcessFlags(lpszFileName, nOpenFlags))
  29. {
  30. }
  31. BOOL CStdioFileEx::Open(LPCTSTR lpszFileName,UINT nOpenFlags,CFileException* pError /*=NULL*/)
  32. {
  33. // Process any Unicode stuff
  34. ProcessFlags(lpszFileName, nOpenFlags);
  35. return CStdioFile::Open(lpszFileName, nOpenFlags, pError);
  36. }
  37. BOOL CStdioFileEx::ReadString(CString& rString)
  38. {
  39. BOOL bReadData;
  40. LPTSTR lpsz;
  41. int nLen = 0; //, nMultiByteBufferLength = 0, nChars = 0;
  42. CString sTemp;
  43. // If at position 0, discard byte-order mark before reading
  44. if (!m_pStream || (GetPosition() == 0 && m_bIsUnicodeText))
  45. {
  46. wchar_t cDummy;
  47. Read(&cDummy, sizeof(wchar_t));
  48. }
  49. // If compiled for Unicode
  50. #ifdef _UNICODE
  51. // Do standard stuff -- both ANSI and Unicode cases seem to work OK
  52. bReadData = CStdioFile::ReadString(rString);
  53. #else
  54. if (!m_bIsUnicodeText)
  55. {
  56. // Do standard stuff -- read ANSI in ANSI
  57. bReadData = CStdioFile::ReadString(rString);
  58. }
  59. else
  60. {
  61. const int nMAX_LINE_CHARS = 4096;
  62. wchar_t* pszUnicodeString = new wchar_t[nMAX_LINE_CHARS];
  63. char* pszMultiByteString= new char[nMAX_LINE_CHARS];
  64. // Read as Unicode, convert to ANSI; fixed by Dennis Jeryd 6/8/03
  65. bReadData = (NULL != fgetws(pszUnicodeString, nMAX_LINE_CHARS, m_pStream));
  66. if (GetMultiByteStringFromUnicodeString(pszUnicodeString, pszMultiByteString, nMAX_LINE_CHARS))
  67. {
  68. rString = (CString)pszMultiByteString;
  69. }
  70. if (pszUnicodeString)
  71. {
  72. delete pszUnicodeString;
  73. }
  74. if (pszMultiByteString)
  75. {
  76. delete pszMultiByteString;
  77. }
  78. }
  79. #endif
  80. // Then remove end-of-line character if in Unicode text mode
  81. if (bReadData)
  82. {
  83. // Copied from FileTxt.cpp but adapted to Unicode and then adapted for end-of-line being just '\r'.
  84. nLen = rString.GetLength();
  85. if (nLen > 1 && rString.Mid(nLen-2) == sNEWLINE)
  86. {
  87. rString.GetBufferSetLength(nLen-2);
  88. }
  89. else
  90. {
  91. lpsz = rString.GetBuffer(0);
  92. if (nLen != 0 && (lpsz[nLen-1] == _T('\r') || lpsz[nLen-1] == _T('\n')))
  93. {
  94. rString.GetBufferSetLength(nLen-1);
  95. }
  96. }
  97. }
  98. return bReadData;
  99. }
  100. // --------------------------------------------------------------------------------------------
  101. //
  102. // CStdioFileEx::WriteString()
  103. //
  104. // --------------------------------------------------------------------------------------------
  105. // Returns: void
  106. // Parameters: LPCTSTR lpsz
  107. //
  108. // Purpose: Writes string to file either in Unicode or multibyte, depending on whether the caller specified the
  109. // CStdioFileEx::modeWriteUnicode flag. Override of base class function.
  110. // Notes: If writing in Unicode we need to:
  111. // a) Write the Byte-order-mark at the beginning of the file
  112. // b) Write all strings in byte-mode
  113. // - If we were compiled in Unicode, we need to convert Unicode to multibyte if
  114. // we want to write in multibyte
  115. // - If we were compiled in multi-byte, we need to convert multibyte to Unicode if
  116. // we want to write in Unicode.
  117. // Exceptions: None.
  118. //
  119. void CStdioFileEx::WriteString(LPCTSTR lpsz)
  120. {
  121. // If writing Unicode and at the start of the file, need to write byte mark
  122. if (m_nFlags & CStdioFileEx::modeWriteUnicode)
  123. {
  124. // If at position 0, write byte-order mark before writing anything else
  125. if (!m_pStream || GetPosition() == 0)
  126. {
  127. wchar_t cBOM = (wchar_t)nUNICODE_BOM;
  128. CFile::Write(&cBOM, sizeof(wchar_t));
  129. }
  130. }
  131. // If compiled in Unicode...
  132. #ifdef _UNICODE
  133. // If writing Unicode, no conversion needed
  134. if (m_nFlags & CStdioFileEx::modeWriteUnicode)
  135. {
  136. // Write in byte mode
  137. CFile::Write(lpsz, lstrlen(lpsz) * sizeof(wchar_t));
  138. }
  139. // Else if we don't want to write Unicode, need to convert
  140. else
  141. {
  142. int nChars = lstrlen(lpsz) + 1; // Why plus 1? Because yes
  143. int nBufferSize = nChars * sizeof(char);
  144. wchar_t* pszUnicodeString = new wchar_t[nChars];
  145. char * pszMultiByteString= new char[nChars];
  146. int nCharsWritten = 0;
  147. // Copy string to Unicode buffer
  148. lstrcpy(pszUnicodeString, lpsz);
  149. // Get multibyte string
  150. nCharsWritten =
  151. GetMultiByteStringFromUnicodeString(pszUnicodeString, pszMultiByteString, ( short ) nBufferSize, GetACP());
  152. if (nCharsWritten > 0)
  153. {
  154. // CFile::Write((const void*)pszMultiByteString, lstrlen(lpsz));
  155. // Do byte-mode write using actual chars written (fix by Howard J Oh)
  156. CFile::Write((const void*)pszMultiByteString,
  157. nCharsWritten*sizeof(char));
  158. }
  159. if (pszUnicodeString && pszMultiByteString)
  160. {
  161. delete [] pszUnicodeString;
  162. delete [] pszMultiByteString;
  163. }
  164. }
  165. // Else if *not* compiled in Unicode
  166. #else
  167. // If writing Unicode, need to convert
  168. if (m_nFlags & CStdioFileEx::modeWriteUnicode)
  169. {
  170. int nChars = lstrlen(lpsz) + 1; // Why plus 1? Because yes
  171. wchar_t* pszUnicodeString = new wchar_t[nChars];
  172. char * pszMultiByteString= new char[nChars];
  173. int nCharsWritten = 0;
  174. // Copy string to multibyte buffer
  175. lstrcpy(pszMultiByteString, lpsz);
  176. nCharsWritten =
  177. GetUnicodeStringFromMultiByteString(pszMultiByteString,
  178. pszUnicodeString, nChars, GetACP());
  179. if (nCharsWritten > 0)
  180. {
  181. // CFile::Write(pszUnicodeString, lstrlen(lpsz) * sizeof(wchar_t));
  182. // Write in byte mode. Write actual number of chars written * bytes (fix by Howard J Oh)
  183. CFile::Write(pszUnicodeString, nCharsWritten*sizeof(wchar_t));
  184. }
  185. else
  186. {
  187. ASSERT(false);
  188. }
  189. if (pszUnicodeString && pszMultiByteString)
  190. {
  191. delete [] pszUnicodeString;
  192. delete [] pszMultiByteString;
  193. }
  194. }
  195. // Else if we don't want to write Unicode, no conversion needed
  196. else
  197. {
  198. // Do standard stuff
  199. //CStdioFile::WriteString(lpsz);
  200. // Do byte-mode write. This avoids annoying "interpretation" of \n's as \r\n
  201. CFile::Write((const void*)lpsz, lstrlen(lpsz)*sizeof(char));
  202. }
  203. #endif
  204. }
  205. UINT CStdioFileEx::ProcessFlags(const CString& sFilePath, UINT& nOpenFlags)
  206. {
  207. m_bIsUnicodeText = false;
  208. // If we have writeUnicode we must have write or writeRead as well
  209. #ifdef _DEBUG
  210. if (nOpenFlags & CStdioFileEx::modeWriteUnicode)
  211. {
  212. ASSERT(nOpenFlags & CFile::modeWrite || nOpenFlags & CFile::modeReadWrite);
  213. }
  214. #endif
  215. // If reading in text mode and not creating... ; fixed by Dennis Jeryd 6/8/03
  216. if (nOpenFlags & CFile::typeText && !(nOpenFlags & CFile::modeCreate) && !(nOpenFlags & CFile::modeWrite ))
  217. {
  218. m_bIsUnicodeText = IsFileUnicode(sFilePath);
  219. // If it's Unicode, switch to binary mode
  220. if (m_bIsUnicodeText)
  221. {
  222. nOpenFlags ^= CFile::typeText;
  223. nOpenFlags |= CFile::typeBinary;
  224. }
  225. }
  226. m_nFlags = nOpenFlags;
  227. return nOpenFlags;
  228. }
  229. // --------------------------------------------------------------------------------------------
  230. //
  231. // CStdioFileEx::IsFileUnicode()
  232. //
  233. // --------------------------------------------------------------------------------------------
  234. // Returns: bool
  235. // Parameters: const CString& sFilePath
  236. //
  237. // Purpose: Determines whether a file is Unicode by reading the first character and detecting
  238. // whether it's the Unicode byte marker.
  239. // Notes: None.
  240. // Exceptions: None.
  241. //
  242. /*static*/ bool CStdioFileEx::IsFileUnicode(const CString& sFilePath)
  243. {
  244. CFile file;
  245. bool bIsUnicode = false;
  246. wchar_t cFirstChar;
  247. CFileException exFile;
  248. // Open file in binary mode and read first character
  249. if (file.Open(sFilePath, CFile::typeBinary | CFile::modeRead, &exFile))
  250. {
  251. // If byte is Unicode byte-order marker, let's say it's Unicode
  252. if (file.Read(&cFirstChar, sizeof(wchar_t)) > 0 && cFirstChar == (wchar_t)nUNICODE_BOM)
  253. {
  254. bIsUnicode = true;
  255. }
  256. file.Close();
  257. }
  258. else
  259. {
  260. // Handle error here if you like
  261. }
  262. return bIsUnicode;
  263. }
  264. unsigned long CStdioFileEx::GetCharCount()
  265. {
  266. int nCharSize;
  267. unsigned long nByteCount, nCharCount = 0;
  268. if (m_pStream)
  269. {
  270. // Get size of chars in file
  271. nCharSize = m_bIsUnicodeText ? sizeof(wchar_t): sizeof(char);
  272. // If Unicode, remove byte order mark from count
  273. nByteCount = (long)GetLength();
  274. if (m_bIsUnicodeText)
  275. {
  276. nByteCount = nByteCount - sizeof(wchar_t);
  277. }
  278. // Calc chars
  279. nCharCount = (nByteCount / nCharSize);
  280. }
  281. return nCharCount;
  282. }
  283. // --------------------------------------------------------------------------------------------
  284. //
  285. // CStdioFileEx::GetUnicodeStringFromMultiByteString()
  286. //
  287. // --------------------------------------------------------------------------------------------
  288. // Returns: int - num. of chars written (0 means error)
  289. // Parameters: char * szMultiByteString (IN) Multi-byte input string
  290. // wchar_t* szUnicodeString (OUT) Unicode outputstring
  291. // int& nUnicodeBufferSize (IN/OUT) Size of Unicode output buffer(IN)
  292. // Actual bytes written to buffer (OUT)
  293. // UINT nCodePage (IN) Code page used to perform conversion
  294. // Default = -1 (Get local code page).
  295. //
  296. // Purpose: Gets a Unicode string from a MultiByte string.
  297. // Notes: None.
  298. // Exceptions: None.
  299. //
  300. int CStdioFileEx::GetUnicodeStringFromMultiByteString(IN char * szMultiByteString, OUT wchar_t* szUnicodeString, IN OUT int& nUnicodeBufferSize, IN UINT nCodePage)
  301. {
  302. int nCharsWritten = 0;
  303. if (szUnicodeString && szMultiByteString)
  304. {
  305. // If no code page specified, take default for system
  306. if (nCodePage == -1)
  307. {
  308. nCodePage = GetACP();
  309. }
  310. try
  311. {
  312. // Zero out buffer first. NB: nUnicodeBufferSize is NUMBER OF CHARS, NOT BYTES!
  313. memset((void*)szUnicodeString, '\0', sizeof(wchar_t) *
  314. nUnicodeBufferSize);
  315. nCharsWritten = MultiByteToWideChar(nCodePage,MB_PRECOMPOSED,szMultiByteString,-1,szUnicodeString,nUnicodeBufferSize);
  316. }
  317. catch(...)
  318. {
  319. TRACE(_T("Controlled exception in MultiByteToWideChar!\n"));
  320. }
  321. }
  322. // Now fix nCharsWritten
  323. if (nCharsWritten > 0)
  324. {
  325. nCharsWritten--;
  326. }
  327. ASSERT(nCharsWritten > 0);
  328. return nCharsWritten;
  329. }
  330. // --------------------------------------------------------------------------------------------
  331. //
  332. // CStdioFileEx::GetMultiByteStringFromUnicodeString()
  333. //
  334. // --------------------------------------------------------------------------------------------
  335. // Returns: int - number of characters written. 0 means error
  336. // Parameters: wchar_t * szUnicodeString (IN) Unicode input string
  337. // char* szMultiByteString (OUT) Multibyte output string
  338. // short nMultiByteBufferSize (IN) Multibyte buffer size
  339. // UINT nCodePage (IN) Code page used to perform conversion
  340. // Default = -1 (Get local code page).
  341. //
  342. // Purpose: Gets a MultiByte string from a Unicode string
  343. // Notes: None.
  344. // Exceptions: None.
  345. //
  346. int CStdioFileEx::GetMultiByteStringFromUnicodeString(wchar_t * szUnicodeString, char* szMultiByteString,
  347. short nMultiByteBufferSize, UINT nCodePage)
  348. {
  349. BOOL bUsedDefChar = FALSE;
  350. int nCharsWritten = 0;
  351. if (szUnicodeString && szMultiByteString)
  352. {
  353. // Zero out buffer first
  354. memset((void*)szMultiByteString, '\0', nMultiByteBufferSize);
  355. // If no code page specified, take default for system
  356. if (nCodePage == -1)
  357. {
  358. nCodePage = GetACP();
  359. }
  360. try
  361. {
  362. nCharsWritten = WideCharToMultiByte(nCodePage, WC_COMPOSITECHECK | WC_SEPCHARS,
  363. szUnicodeString,-1, szMultiByteString, nMultiByteBufferSize, sDEFAULT_UNICODE_FILLER_CHAR, &bUsedDefChar);
  364. }
  365. catch(...)
  366. {
  367. TRACE(_T("Controlled exception in WideCharToMultiByte!\n"));
  368. }
  369. }
  370. // Now fix nCharsWritten
  371. if (nCharsWritten > 0)
  372. {
  373. nCharsWritten--;
  374. }
  375. return nCharsWritten;
  376. }