[英]C++ - Append text to a file with NTFS “append data” only permissions
[英]C++ and NTFS file: pathname VS opening
這是此問題的擴展: fstream不會打開路徑名帶有重音符號的文件
問題如下:一個程序打開一個簡單的NTFS文本文件,該文件的路徑名帶有重音符號 (例如à , ò ,...)。 在我的測試中我使用的是路徑文件I:\\ UNIVERSITA \\ foo.txt的 (UNIVERSITA是意大利大學的翻譯)
以下是測試程序:
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>
#include <errno.h>
#include <Windows.h>
using namespace std;
LPSTR cPath = "I:/università/foo.txt";
LPWSTR widecPath = L"I:/università/foo.txt";
string path("I:/università/foo.txt");
void tryWithStandardC();
void tryWithStandardCpp();
void tryWithWin32();
int main(int argc, char **argv) {
tryWithStandardC();
tryWithStandardCpp();
tryWithWin32();
return 0;
}
void tryWithStandardC() {
FILE *stream = fopen(cPath, "r");
if (stream) {
cout << "File opened with fopen!" << endl;
fclose(stream);
}
else {
cout << "fopen() failed: " << strerror(errno) << endl;
}
}
void tryWithStandardCpp() {
ifstream s;
s.exceptions(ifstream::failbit | ifstream::badbit | ifstream::eofbit);
try {
s.open(path.c_str(), ifstream::in);
cout << "File opened with c++ open()" << endl;
s.close();
}
catch (ifstream::failure f) {
cout << "Exception " << f.what() << endl;
}
}
void tryWithWin32() {
DWORD error;
HANDLE h = CreateFile(cPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE) {
error = GetLastError();
cout << "CreateFile failed: error number " << error << endl;
}
else {
cout << "File opened with CreateFile!" << endl;
CloseHandle(h);
return;
}
HANDLE wideHandle = CreateFileW(widecPath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (wideHandle == INVALID_HANDLE_VALUE) {
error = GetLastError();
cout << "CreateFileW failed: error number " << error << endl;
}
else {
cout << "File opened with CreateFileW!" << endl;
CloseHandle(wideHandle);
}
}
源文件以UTF-8編碼保存。 我正在使用Windows 8。
這是使用VC ++編譯的程序的輸出(Visual Studio 2012)
fopen() failed: No such file or directory
Exception ios_base::failbit set
CreateFile failed: error number 3
CreateFileW failed: error number 3
這是使用MinGW g ++的輸出
fopen() failed: No such file or directory
Exception basic_ios::clear
CreateFile failed: error number 3
File opened with CreateFileW!
因此,讓我們來看以下問題:
我希望可以使用通用路徑名打開通用文件,而無需使用特定於平台的代碼,但是我不知道該怎么做。
提前致謝。
你寫:
“源文件以UTF-8編碼保存。”
好吧(到目前為止),如果您使用的是g ++編譯器,該編譯器以UTF-8作為默認的基本源字符集。 但是,Visual C ++默認情況下將假定源文件是使用Windows ANSI編碼的,除非另有明確說明。 因此,請確保在開始時具有BOM(字節順序標記),據我所知,BOM(字節記錄)尚未公開,這會導致Visual C ++將其視為使用UTF-8編碼。
然后你問,
“1。 為什么fopen()和std :: ifstream在Linux中進行類似的測試卻在Windows中卻沒有?”
對於Linux來說,它可能會起作用,因為(1)現代Linux是面向UTF-8的,因此,如果文件名看起來相同,則很可能與源代碼中看起來相同的UTF-8編碼文件名相同,並且(2)在*中nix文件名只是一個字節序列,而不是字符序列。 這意味着無論外觀如何,如果傳遞相同的字節序列,相同的值,則表示匹配,否則不匹配。
相反,在Windows中,文件名是可以用各種方式編碼的字符序列。
在您的情況下,源代碼中UTF-8編碼的文件名以Windows ANSI的形式存儲在可執行文件中(是的,使用Visual C ++進行編譯的結果取決於Windows中所選的ANSI代碼頁,據我所知,這也是未記錄的)。 然后,將此gobbledegook字符串向下傳遞到例程層次結構中,並轉換為UTF-16,這是Windows中的標准字符編碼。 結果根本與文件名不匹配。
您進一步問,
“2。 為什么CreateFileW()僅能與g ++一起編譯?”
大概是因為在源代碼文件的開頭沒有包含BOM(請參見上文)。
使用BOM,至少在Windows 7中,一切都可以與Visual C ++很好地配合使用:
File opened with fopen! File opened with c++ open() File opened with CreateFile!
最后,你問,
“3。 是否存在跨平台替代CreateFile的選擇?”
並不是的。 有Boost文件系統。 但是,盡管其版本2確實針對標准庫的基於有損窄字符的編碼提供了一種解決方法,但該解決方法在版本3中已刪除,該版本僅使用標准庫的Visual C ++ 擴展,其中Visual C ++實現提供了流的寬字符參數版本構造函數並open
。 即,至少就我所知(最近我還沒有檢查是否已解決問題),Boost文件系統通常只適用於Visual C ++,而不適用於g ++,盡管它適用於無麻煩字符的文件名。
v2的解決方法是嘗試轉換為Windows ANSI(由GetACP
函數指定的代碼頁),如果該方法不起作用,請嘗試GetShortPathName
,實際上可以保證可以用Windows ANSI表示。
據我了解,刪除Boost文件系統中的變通辦法的部分原因是,從原則上講,用戶至少在Windows Vista和更早版本中可以關閉Windows簡稱功能。 但是,這不是實際問題。 這只是意味着,如果用戶由於故意破壞了系統而遇到問題,則可以使用一個簡單的修復程序(即將其重新打開)。
您遇到的問題是,當path特定於實現時,傳遞給fstreams的編碼。 此外,程序的行為是實現定義的,因為它使用代碼中C ++字符集之外的字符,即重音字符。 那里的問題是,有許多不同的編碼可用於表示這些字符。
現在,有解決方案:
wchar_t const path[] = {'f', 0x20ac, '.', 't', 'x', 't'};
。 這並不是很舒服,但實際上,路徑是使用某些Unicode編碼或用戶輸入存儲在文件中的。 請注意,盡管wchar_t路徑不可移植,但將它們移植到新平台通常不是很復雜。 幾個#ifdefs就可以了。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.