《C++字符串完全指南——第一部分:win32 字符編碼》

《C++字符串完全指南--第一部分:win32?字符編碼》????

原作者:Michael?Dun

????? 譯??? 者:Dingqiao?Wang??

引言

毫無疑問,你肯定見過像TCHAR,?std::string,?BSTR等等這類字符串類型.也包括一些以_tcs開頭的奇怪的宏。也許你正盯著屏幕"哇哇"的發愁,然而閱讀完本文情況將會改觀。這篇指南概述了引入各種類型字符串的目的,展示了它們的一些簡單用法,同時描述了在必要的時候在它們之間如何進行轉換。

在第一部分,將會涉及三種類型的字符編碼。理解編碼方案的工作原理對你至關重要。即使你現在已經知道字符串是一個字符數組,還是看看這部分內容。一旦你閱讀了這些內容,你就清楚了這么多字符串類之間的聯系。

在第二部分,將描述字符串類本身,包括什么時候使用哪種類以及如何在他們之間進行轉換。

基本字符-----ASCIIDBCSUnicode

所有的字符串類最終都歸結為C風格的字符串,而C風格的字符串就是字符數組,因此我首先介紹下字符類型。有三種編碼方案和三種字符類型。第一種方案是單字節編碼(single-byte?character?set,?or?SBCS).這種方案里,所有字符都正好是一個字節長。ASCII碼就是單字節編碼的例子。單字節字符串以一個字節的0做結束標志。

第二種編碼方案是多字節編碼(multi-byte?character?set,?or?MBCS).在多字節編碼中包含一些單字節長的字符,也包含其它的多余一個字節長度的字符。在Windows中使用的多字節編碼方案中包含兩種類型,單字節和雙字節類型。由于在Windows中使用到的最長的多字節字符也就是2個字節長,因此常常用雙字節字符集(double-byte?character?set,?or?DBCS)來代替MBCS.

在雙字節編碼方式中,一些值被保留來指示他們是雙字節的一部分。舉個例子,在Shift-JIS編碼(一種常用的日文編碼方案)中,介于0x81-0x9F?and?0xE0-0xFC之間的值就用來說明這是雙字節字符,它的下一個字節是其一部分。這些值被稱作"頭部字節"(lead?bytes),他們總是比0X7F大。緊跟在頭部字節后的下一字節被稱作"后隨字節"(trail?bytes)。在雙字節編碼中,后隨字節可以為任意非零值。和單字節編碼一樣,雙字節編碼使用單字節的0值作為結束符。

第三種方案是UnicodeUnicode?是一種所有字符均采用二個字節的編碼標準。Unicode字符有時也被稱作寬字節(wide?characters),因為他們比單字節占用更多存貯。注意,Unicode并不是一種多字節編碼——多字節編碼的顯著特點是字符是不同長度的。一個Unicode字符串以兩個0值字節作為結束標志(0值的寬字符形式)

單字節字符包括拉丁文字母,帶重音的字符(accented?characters),ASCII標準和DOS系統中定義的圖形符號。雙字節字符在東亞和中東地區的語言中使用。UnicodeCOMWindows?NT?內部使用。

你肯定已經很熟悉單字節字符了。當你在使用char類型時,處理的就是單字節字符。雙字節字符也用過char類型來操作(這也是我們使用雙字節時遇到的第一個怪現象)wchar_t類型代表著Unicode字符。Unicode字符和字符串字面值由一個前綴字母L來編寫,例如:

[cpp] view plaincopyprint?
  1. wchar_t??wch?=?L'1';??????//?2?bytes,?0x0031??
  2. ??
  3. ?wchar_t*?wsz?=?L"Hello";??//?12?bytes,?6?wide?characters??

字符在內存中是如何存儲

單字節字符串在內存中是以一個字符接著一個字符,用單字節的0來結束的形式存儲的。

例如,"Bob"是這樣存儲的:

42?

?6F?

?62?

?00?

B

o

b

EOS

Unicode?版本的,L"Bob",是這樣存儲的:

42?00?

?6F?00?

?62?00?

?00?00?

B

o

b

EOS

以0x0000(0Unicode編碼形式)作為結束標記.

雙字節字符串初看起來像單字節字符串,但是當我們以后使用字符串操作函數和利用指針遍歷字符串時將看到他們的細微區別。字符串("nihongo")采用以下形式存貯(下面表中的LB代表?lead?bytes,TB代表trail?bytes):


記住,"ni"值并不是被解釋為0xFA93這一值。而是93FA兩個值以那種字節序,在一起而被編碼為"ni".(因此在一個大端格式(Big-endian)CPU上,這些字節仍然按上述順序)

字符串處理函數的使用

我們已經見過C風格字符串函數像strcpy(),?sprintf(),?atol()等等。這些函數只能用于處理單字節的字符串。標準庫中有他們的只能用于處理Unicode字符串的版本,諸如wcscpy(),?swprintf(),?_wtol().

微軟也在C運行庫(C runtime library)中增加了這些函數處理多字節字符串的版本。strxxx()這類函數對應的DBCS版本取名為_mbsxxx().如果你遇到了DBCS字符串(如果你的軟件是安裝在日文、中文或者其他使用DBCS的語言情況下你會遇到的),你應該總是使用_mbsxxx()函數,因為他們接受SBCS字符串(一個DBCS字符串可能僅僅包含單字節字符,這就是_mbsxxx()函數可以處理SBCS字符串的緣故)

讓我們來看一個典型的字符串來解釋字符串處理函數不同版本的必要性。回到上文講到的Unicode字符串L"Bob":

?42?00?

?6F?00?

?62?00?

?00?00?

B

o

b

EOS

因為x86系列CPU是小端格式(little-endian),0x0042在內存中形式為42?00.你預見到了把這個字符串傳遞給函數strlen()的問題了嗎?函數將看到頭字節42,然后00,而00恰好是字符串結束標志,函數將返回1.相反,將"Bob"傳遞給函數wcslen(),將變得更糟。wcslen()會首先看到0x6F42,然后是0x0062,繼而一直讀下去直到碰到了00?00序列或者引起了GPF.

這里我們涉及到了strxxx()wcsxxx()的對比。他們的區別又是什么呢?他們的區別至關重要,與在DBCS字符串中的合理的遍歷密切相關。下文將講述字符串的遍歷,然后再回到二者的對比上來。

字符串中合理的遍歷和索引

我們之中的大部分人都是伴著SBCS字符串而成長起來的,我們習慣了利用指針通過++--操作符來遍歷一個字符串。我們也習慣于用數組來獲取字符串中的字符。這兩種方式在SBCSUnicode字符串下用起來十分完美,因為字符都是相同長度的,編譯器會成功返回我們想要的字符。

但是,當你遇到了DBCS字符串時,為了代碼的正常運行,你必須改掉這種習慣。

這里有兩條利用指針遍歷DBCS字符串的原則。破壞了這些原則將導致你大部分與DBCS相關的漏洞(bugs)。

1.不要使用++操作符來向前遍歷,除非你一直檢查字符串的頭字節。

2.永遠不要用--操作符來向后遍歷。

我先解釋原則2,因為很容易找到一個破壞它的而不知不覺的例子。假設你有一個程序在自己的目錄里存貯配置文件,而你把安裝目錄寫入了注冊表里。在運行時,你讀取安裝目錄,附加上配置文件名,然后嘗試讀取它。再假設你的安裝目錄是C:\Program?Files\MyCoolApp,要建立的文件名是C:\Program?Files\MyCoolApp\config.bin,在你測試的時候它工作的很完美。

現在,假想以下是你用來建立文件名的代碼:

[cpp] view plaincopyprint?
  1. bool?GetConfigFileName?(?char*?pszName,?size_t?nBuffSize?)??
  2. ??
  3. {??
  4. ??
  5. char?szConfigFilename[MAX_PATH];??
  6. ??
  7. ???
  8. ??
  9. ????//?Read?install?dir?from?registry...?we'll?assume?it?succeeds.??
  10. ??
  11. ???
  12. ??
  13. ????//?Add?on?a?backslash?if?it?wasn't?present?in?the?registry?value.??
  14. ??
  15. ????//?First,?get?a?pointer?to?the?terminating?zero.??
  16. ??
  17. char*?pLastChar?=?strchr?(?szConfigFilename,?'\0'?);??
  18. ??
  19. ???
  20. ??
  21. ????//?Now?move?it?back?one?character.??
  22. ??
  23. ????pLastChar--;????
  24. ??
  25. ???
  26. ??
  27. ????if?(?*pLastChar?!=?'\\'?)??
  28. ??
  29. ????????strcat?(?szConfigFilename,?"\\"?);??
  30. ??
  31. ???
  32. ??
  33. ????//?Add?on?the?name?of?the?config?file.??
  34. ??
  35. ????strcat?(?szConfigFilename,?"config.bin"?);??
  36. ??
  37. ???
  38. ??
  39. ????//?If?the?caller's?buffer?is?big?enough,?return?the?filename.??
  40. ??
  41. ????if?(?strlen?(?szConfigFilename?)?>=?nBuffSize?)??
  42. ??
  43. ????????return?false;??
  44. ??
  45. ????else??
  46. ??
  47. ????????{??
  48. ??
  49. ????????strcpy?(?pszName,?szConfigFilename?);??
  50. ??
  51. ????????return?true;??
  52. ??
  53. ????????}??
  54. ??
  55. }??

雖然這是一分很安全的代碼,但是遇到一些特殊的DBCS字符時,仍將會出錯。來分析下為什么會這樣,假設一個日本用戶將你的安裝目錄改為.以下是目錄名在內存中的存貯形式:

當GetConfigFileName()檢查反斜杠時,它會檢查安裝目錄的最后一個非0字節,來判斷是否等于"\\",如果沒有則添加上去。運行的結果是返回錯誤的文件名。哪兒出錯呢?看看以藍色高亮顯示的反斜杠。反斜杠字符的值是0x5C.的值是83 5C,而上述代碼誤將它的后隨字節當做了一個獨立字符。正確的向后遍歷方法是使用注意到DBCS字符特點的函數,使指針移動正確數目的字節。下面是正確的代碼,指針移動部分用紅色標記了。

[cpp] view plaincopyprint?
  1. bool?FixedGetConfigFileName?(?char*?pszName,?size_t?nBuffSize?)??
  2. ??
  3. {??
  4. ??
  5. char?szConfigFilename[MAX_PATH];??
  6. ??
  7. ???
  8. ??
  9. ????//?Read?install?dir?from?registry...?we'll?assume?it?succeeds.??
  10. ??
  11. ??
  12. ????//?Add?on?a?backslash?if?it?wasn't?present?in?the?registry?value.??
  13. ??
  14. ????//?First,?get?a?pointer?to?the?terminating?zero.??
  15. ??
  16. ???char*?pLastChar?=?_mbschr?(?szConfigFilename,?'\0'?);??
  17. ??
  18. ??
  19. ????//?Now?move?it?back?one?double-byte?character.??
  20. ??
  21. ????pLastChar?=?CharPrev?(?szConfigFilename,?pLastChar?);??
  22. ??
  23. ????if?(?*pLastChar?!=?'\\'?)??
  24. ??
  25. ????????_mbscat?(?szConfigFilename,?"\\"?);??
  26. ??
  27. ???
  28. ????//?Add?on?the?name?of?the?config?file.??
  29. ??
  30. ????_mbscat?(?szConfigFilename,?"config.bin"?);??
  31. ??
  32. ?????//?If?the?caller's?buffer?is?big?enough,?return?the?filename.??
  33. ??
  34. ????if?(?_mbslen?(?szInstallDir?)?>=?nBuffSize?)??
  35. ??
  36. ????????return?false;??
  37. ??
  38. ????else??
  39. ??
  40. ????????{??
  41. ??
  42. ????????_mbscpy?(?pszName,?szConfigFilename?);??
  43. ??
  44. ????????return?true;??
  45. ??
  46. ????????}??
  47. ??
  48. }??

修改后的函數使用了CharPrev() API來使pLastChar向后移動一個字符,這樣就可能移動兩個字節如果字符串以雙字節字符結尾。在這個版本中,假設的情況會運行正常,因為頭部字節將永遠不等于0x5C。
你可以合理想象下破壞原則1的方式。舉個例子,你通過判斷字符':'出現的次數驗證用戶輸入的一個文件名是否合法。如果你使用++而不是CharNext()來遍歷,你可能會產生錯誤如果碰巧遇到后隨字節等于':'的字符。
和原則2相關的使用數組索引的原則:
2a.永遠不要使用減法來計算字符串的索引。
破壞這個原則的代碼和破壞原則2的代碼很相似。例如,pLastChar像下面這樣使用時:

[cpp] view plaincopyprint?
  1. char*?pLastChar?=?&szConfigFilename?[strlen(szConfigFilename)?-?1];??

這同樣的破壞了原則,因為計算索引時使用減1這等于指針向后移動一個字節,這破壞了原則2.

再談strxxx()和_mbsxxx()的對比
現在應該明白_mbsxxx()這類函數的必要性了。Strxxx()不知道DBCS字符而_mbsxxx()函數了解.如果你調用將返回錯誤結果 ,但是_mbsxxx()將在末尾識別出雙字節字符,返回實際上指向反斜杠的指針。 關于字符串函數的最后一點,strxxx()和_mbsxxx()函數取或者返回長度均以char為單位。 因此對于一個包含3個雙字節字符的字符串,_mbslen()將返回6.Unicode函數以wchar_t為單位返回長度,例如wcslen(L"Bob")返回3.

Win32?API中的MBCSUnicode

兩套API

即使你從沒有注意到,但是Win32中每一個處理字符串的API和消息都有兩個版本.

一個接受MBCS字符串,另一個接受Unicode字符串。舉個例子,并沒有SetWindowText這個API,相反,有SetWindowTextA()和SetWindowTextW().后綴A(對于ANSI)指示MBCS函數,后綴W(對于Wide)指示Unicode版本。

當你建立一個Windows應用程序,你可以選擇使用MBCS或者Unicode版本的API.如果你使用VC應用程序向導并且從未接觸過編譯器設置的話,你使用的一直是MBCS版本。那么為什么我們寫下"SetWindowText"而事實上又沒有這個名字對應的API呢?在winuser.h頭文件中包含了一些#define開頭的宏,如下:


[cpp] view plaincopyprint?
  1. BOOL?WINAPI?SetWindowTextA?(?HWND?hWnd,?LPCSTR?lpString?);??
  2. ??
  3. BOOL?WINAPI?SetWindowTextW?(?HWND?hWnd,?LPCWSTR?lpString?);??
  4. ??
  5. #ifdef?UNICODE??
  6. ??
  7. #define?SetWindowText??SetWindowTextW??
  8. ??
  9. #else??
  10. ??
  11. #define?SetWindowText??SetWindowTextA??
  12. ??
  13. #endif??


當以MBCS API建立時,UNICODE就沒有定義,因此編譯器看到:

[cpp] view plaincopyprint?
  1. #define?SetWindowText??SetWindowTextA??

并將所有調用SetWindowText()的地方用真正的API,SetWindowTextA來替換掉。(注意你可以直接調用函

數SetWindowTextA和SetWindowTextW,盡管你很少需要這樣做.)

因此,如果你想要把Unicode API設定為默認的話,你可轉到編譯器設置項,從預定義符號表中移除_MBCS

符號,同時添加上UNICODE和_UNICODE.(你應該把兩個都加上,因為不同頭文件使用不同符號.)但是,如

果你直接使用char作為字符串的話,將會遇到麻煩。

考慮以下代碼:


[cpp] view plaincopyprint?
  1. HWND?hwnd?=?GetSomeWindowHandle();??
  2. ??
  3. char?szNewText[]?=?"we?love?Bob!";??
  4. ??
  5. SetWindowText?(?hwnd,?szNewText?);??


當編譯器將"SetWindowText"用"SetWindowTextW"替換后,代碼變為:

[cpp] view plaincopyprint?
  1. HWND?hwnd?=?GetSomeWindowHandle();??
  2. ??
  3. char?szNewText[]?=?"we?love?Bob!";??
  4. ??
  5. SetWindowTextW?(?hwnd,?szNewText?);??

看到問題所在呢嗎?我們向需要Unicode字符串的函數傳遞了一個單字節字符串。解決這種問題的第一種方法就是在字符串變量定義的周圍使用#ifdef宏:


[cpp] view plaincopyprint?
  1. HWND?hwnd?=?GetSomeWindowHandle();??
  2. ??
  3. #ifdef?UNICODE??
  4. ??
  5. wchar_t?szNewText[]?=?L"we?love?Bob!";??
  6. ??
  7. #else??
  8. ??
  9. char?szNewText[]?=?"we?love?Bob!";??
  10. ??
  11. #endif??
  12. ??
  13. SetWindowText?(?hwnd,?szNewText?);??


你肯定會為在每個字符串代碼周圍加上這些宏而頭疼不已。問題的解決方案就是使用TCHAR.
TCHAR?
大救星

TCHAR是一種允許你為MBCSUnicode應用使用同一分代碼的字符類型,它不需要在你整個代碼中寫這些零亂的#define宏。TCHAR的一種定義如下:


[cpp] view plaincopyprint?
  1. #ifdef?UNICODE??
  2. typedef?wchar_t?TCHAR;??
  3. #else??
  4. typedef?char?TCHAR;??
  5. #endif??

因此一個TCHAR在MBCS工程中是char類型,在Unicode工程中是wchar_t類型。這里還有一個_T()宏,來處理Unicode字符串字面值所需的L前綴。

[cpp] view plaincopyprint?
  1. <pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre?name="code"?class="cpp"><pre>??

[cpp] view plaincopyprint?
  1. <span?style="color:#000000;"></span><pre?name="code"?class="cpp">#ifdef?UNICODE??
  2. ??
  3. #define?_T(x)?L##x??
  4. ??
  5. #else??
  6. ??
  7. #define?_T(x)?x??
  8. ??
  9. #endif??

##是用來連接兩個參數的預編譯操作符。無論何時,在你代碼中有字符串字面值時,使用_T宏,那么在Unicode工程中就會添加上L前綴。

[cpp] view plaincopyprint?
  1. <p></p><pre?name="code"?class="cpp"><pre?name="code"?class="cpp">TCHAR?szNewText[]?=?_T("we?love?Bob!");??

正如有隱藏SetWindowTextA/W的宏一樣,也有一些宏可以用來代替使用strxxx()?_mbsxxx()字符串函數.例如,你可以使用_tcsrchr宏來替換strrchr()或者_mbsrchr或者wcsrchr._tcsrchr根據是否定義了_MBCS或者UNICODE符號而被展開為具體對應的函數,就像SetWindowText那樣。

不止strxxx()函數由TCHAR,還有很多,例如_stprintf()( 替換Sprintf()swprintf() ),_tfopen()( 替換fopen()_wfopen()? ).所有的宏定義列表在MSDN"Generic-Text?Routine?Mappings"主題下可查.

StringTCHAR?typedef

由于Win32?API文檔以函數名列舉函數(l例如,"SetWindowText"),所有的字符串均以TCHAR形式給定。(例外之處是xp系統中的僅適用于UnicodeAPI)

下列是你可在MSDN中看到的常見typedef:

何時使用TCHARUnicode

那么講了這么多,你可能會想"為什么我要使用Unicode?我已經單單使用char好多年了"

下面三種情況使用Unicode將會頗有益處:

1.你的程序僅僅在Windows?NT系統上運行。

2.?你的程序要處理長度超過MAX_PATH的文件名。

3.你的程序使用了Windows?XP中新的API,而這些APi沒有區分的A/W版本。

大部分的Unicode?API都沒有在Windows?9x上執行,所以如果你只想你的程序在9x上運行,那你就要堅持使用MBCS?API.(微軟公司一些新的叫做MicroSoft?Layer的庫,允許在9X上使用Unicode?API,但是我沒有使用過,我不知道執行情況如何.)但是,既然NT系統內部所有的都采用Unicode,使用Unicode?API可以提高你程序運行的速度。每次你向MBCS?API傳遞一個字符串時,系統將字符串轉換為Unicode型,同時調用相應的Unicode?API。如果一個字符串返回了,那么操作系統將其轉換后再返回。盡管這些轉換操作都做了很大程度的優化來盡可能減小影響,但是鑒于其影響運行速度還是應該避免。

NT?只有在你使用Unicode?API時才允許使用超過MAX_PATH長度的文件名。使用Unicode?API?的好處一方面就是你的程序將自動處理不同用戶鍵入的任意語言。那么,當一個用戶可同時鍵入一個英文的、中文的、日文的文件名,而你可以不用編寫任何特別處理的代碼,因為它們對你而言都是Unicode字符。

最后,隨著Windows?9x的下線,微軟好像已經廢除了MBCS? API。例如,SetWindowTheme()?API,有兩個字符串參數,但是只有Unicode版本。使用Unicode工程將簡化你的字符串處理,因為你也不想在MBCSUnicode之間來回轉換。

而且即使你現在沒有建立Unicode工程,你也應該一直使用TCHAR和相關的宏。

不僅僅因為這樣可以保證你代碼的DBCS安全性,同時當未來某個時候你想建立Unicode工程時,你只需改動一下你編譯器的設置!


原文地址:http://www.codeproject.com/Articles/2995/The-Complete-Guide-to-C-Strings-Part-I-Win32-Chara

下一部分原文地址:http://www.codeproject.com/Articles/3004/The-Complete-Guide-to-C-Strings-Part-II-String-Wra

下一部分譯文還在翻譯中...

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/452218.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/452218.shtml
英文地址,請注明出處:http://en.pswp.cn/news/452218.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring、Spring MVC、MyBatis整合文件配置詳解

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 web.xml的配置 web.xml應該是整個項目最重要的配置文件了&#xff0c;不過servlet3.0中已經支持注解配置方式了。在servlet3.0以前每…

19.C++-(=)賦值操作符、初步編寫智能指針

()賦值操作符 編譯器為每個類默認重載了()賦值操作符默認的()賦值操作符僅完成淺拷貝默認的賦值操作符和默認的拷貝構造函數有相同的存在意義()賦值操作符注意事項 首先要判斷兩個操作數是否相等 返回值一定是 return *this; 返回類型是Type&型,避免連續使用后,出現bug 比如…

windows mysqldump 不成功 1049 1064 報錯

1064 路徑不對&#xff0c;需要cd選到mysql bin目錄下 1049 在cmd里面不需要分號 以下是正確的 E:\phpStudy\PHPTutorial\MySQL\bin>mysqldump -uroot -proot db >db.sql 轉載于:https://www.cnblogs.com/JANCHAN/p/9227388.html

學成在線--14.使用RabbitMQ完成頁面發布

文章目錄一.技術方案二.頁面發布——消費方1.需求分析2.創建Cms Client工程1&#xff09;創建maven工程2&#xff09;配置文件3&#xff09;啟動類3.RabbitmqConfig配置類4.定義消息格式5.PageDao1&#xff09;使用CmsPageRepository 查詢頁面信息2&#xff09;使用CmsSiteRepo…

對象模型中類與類間的關系

類與類之間通常有關聯、聚集、泛化(繼承)、依賴和細化4種關系 1.關聯 關聯表示兩個類的對象之間存在某種語義上的聯系。 (1) 普通關聯 只要在類與類之間存在連接關系就可以用普通關聯表示。普通關聯的圖示符號是連接兩個類之間的直線&#xff0c;如下圖所示。關聯…

記憶講師石偉華微信公眾號2017所有文章匯總(待更新)

17-10-24-不勝光榮的記憶 17-10-26-每日一個超長英文單詞&#xff08;2&#xff09; 17-10-27-每日一個超長英文單詞&#xff08;3&#xff09; 17-10-28-每日一個超長英文單詞&#xff08;4&#xff09; 轉載于:https://www.cnblogs.com/bakblog/p/9228096.html

Log4J日志配置詳解

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、Log4j簡介 Log4j有三個主要的組件&#xff1a;Loggers(記錄器)&#xff0c;Appenders (輸出源)和Layouts(布局)。這里可簡單理解為日…

中文編碼雜談

編碼問題的例子 在windows自帶的notepad&#xff08;記事本&#xff09;程序中輸入“聯通”兩個字&#xff0c;保存后再次打開&#xff0c;會發現“聯通”不見了&#xff0c;代之以“”的亂碼。這是windows平臺上典型的中文編碼問題。即文件保存的時候是按照ANSI編碼&#xff…

Java NIO (十四)NIO 和 IO 的區別和適用場景分析

在研究Java NIO和IO API時&#xff0c;很快就會想到一個問題&#xff1a; 什么時候應該使用IO&#xff0c;什么時候應該使用NIO&#xff1f; 在本文中&#xff0c;我將嘗試闡明Java NIO和IO之間的區別&#xff0c;它們的用例以及它們如何影響代碼的設計。 ###Java NIO和IO之間的…

面向對象三種模型之間的關系

功能模型指明了系統應該“做什么”&#xff1b;動態模型明確規定了什么時候(即在何種狀態下接受了什么事件的觸發)做&#xff1b;對象模型則定義了做事情的實體。在面向對象方法學中&#xff0c;對象模型是最基本最重要的&#xff0c;它為其他兩種模型奠定了基礎&#xff0c;人…

android node

pkg install nodejs-current轉載于:https://www.cnblogs.com/insight0912/p/9231342.html

springmvc 中@Controller和@RestController的區別

1.Controller, RestController的共同點 都是用來表示Spring某個類的是否可以接收HTTP請求 2.Controller, RestController的不同點 Controller標識一個Spring類是Spring MVC controller處理器 RestController&#xff1a; a convenience annotation that does nothing more …

easyUI 日期控件修改...

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 個人覺得easyUI挺好用的。 它的中文文檔地址&#xff1a; http://www.zi-han.net/case/easyui/ 日期本來效果是這樣的&#xff1a; 改…

面向對象分析的三個模型與5個層次

在面向對象分析中&#xff0c;主要由對象模型、動態模型和功能模型組成。對象模型是最基本、最重要、最核心的。 面向對象建模得到的模型包含系統的3個要素&#xff0c;即靜態結構(對象模型)、交互次序(動態模型)和數據變換(功能模型)。解決的問題不同&#xff0c;這3個子模型…

學成在線--15.課程計劃查詢

文章目錄一.需求分析二.頁面原型1.tree組件介紹2.webstorm配置jsx三.API接口1.數據模型2.自定義模型類3.接口定義四.sql語句五.服務器端1.Dao1&#xff09;Mapper接口2&#xff09;Mapper映射文件2.Service3.Controller4.測試六.前端1.Api方法2.Api調用1&#xff09;定義查詢課…

團隊作業-項目答辯

1. 王書磊 1600802063 http://www.cnblogs.com/wsl-1117/ 劉令斌 1600802017 http://www.cnblogs.com/liulingbin/ 許浩然 1600802066 https://www.cnblogs.com/xuhaoran1/ 成明龍 1600802038 http://www.cnblogs.com/CMLCML/ 2這是我們的效果圖. 3.&#xff08;1&#xff09;修…

Java構造和解析Json數據的兩種方法詳解一

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 在www.json.org上公布了很多JAVA下的json構造和解析工具&#xff0c;其中org.json和json-lib比較簡單&#xff0c;兩者使用上差不多但還是…

面向對象方法開發的方法

面向對象分析首要的工作&#xff0c;是建立問題域的對象模型。 這個模型描述了現實世界中的“類與對象”以及它們之間的關系&#xff0c;表示了目標系統的靜態數據結構。靜態數據結構對應用細節依賴較少&#xff0c;比較容易確定。因此&#xff0c;用面向對象方法開發絕大多數…

程序員編程需要多少個小時?

Michael Arrington曾發表一篇博文說&#xff0c;創業者必須加倍的努力工作&#xff0c;甚至不惜趴在辦公桌上睡覺&#xff0c;這樣才能成功。對此&#xff0c;我并不贊同其觀點&#xff0c;我看了很多評論都是關于這樣工作會適得其反&#xff0c;不但沒有獲得成功&#xff0c;相…

事務以及@Transcational注解

文章目錄1.事務的概念2.事務的四個特性3.關于Transcational注解的理解4.使用場景5.舉例6.編程式事務管理7.相關知識1.事務的概念 事務&#xff0c;是指作為單個邏輯工作單元執行的一系列操作&#xff0c;結果只有成功和失敗兩種&#xff0c;要么全部成功(全部提交)&#xff0c…