2014-11-24 23 views
3

當MMAP()荷蘭國際集團的文本文件,就像這樣如何解決從mmap()返回的字符串中缺少NUL終止符的問題?

int fd = open("file.txt", O_RDWR); 
fstat(fd, &sb) 
char *text = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 

文件內容被直接映射到存儲器中,並text它將不包含一個NUL - 終止子與正常字符串函數在其上,以便操作不會安全。在Linux上(至少)未使用頁面的剩餘字節是零填充的,所以在文件大小不是頁面大小倍數的所有情況下,您都可以得到NUL終止符。

但依靠感覺髒和其他mmap()實現(例如,在FreeBSD中,我認爲)不會零填充部分頁面。映射頁面大小倍數的文件也將缺少NUL終止符。

是否有合理的方法來解決這個問題或添加NUL終結符?

事情我已經考慮

  1. 使用strn*()功能完全和跟蹤距離的緩衝區的末尾。
    • 優點:無需NUL終止
    • 缺點:需要額外的追蹤知道距離解析文本何時結束的文件;一些str*()功能沒有strn*()對應,如strstr
  2. 由於建議使用another answer,請在文本文件映射後的固定地址進行匿名映射。
    • 優點:可以使用常規的C str*()功能
    • 缺點:使用MAP_FIXED不是線程安全的;看起來像一個可怕的黑客無論如何
  3. mmap()mmap()一個額外的字節,使地圖可寫,並寫入NUL終止符。 OpenGroup的mmap man page表示,您可以製作比對象大小更大的映射,但訪問實際映射對象之外的數據將生成SIGBUS
    • 優點:可以使用常規的C str*()功能
    • 缺點:(?忽略)需要處理SIGBUS,這可能意味着別的事情。我不確定編寫NUL終結符會起作用嗎?
  4. 將頁面大小倍數爲ftruncate()的文件擴展一個字節。
    • 優點:可以使用常規的C str*()功能; ftruncate()會爲你新分配的區域寫入NUL字節
    • 缺點:我們必須寫入文件,這在所有情況下都是不可能或不可接受的;對於mmap()實現不填零
  5. 只是read()文件放入一些malloc()「,忘記了D內存部分頁面不能解決問題有關mmap()
    • 優點:避免了所有的這些解決方案;爲NUL易malloc()和額外的字節
    • 缺點:比不同的性能特徵mmap()

解決方案#1通常似乎是最好的,只是需要在功能讀取的部分一些額外的工作文本。

有更好的選擇,還是這些是最好的解決方案?我沒有考慮過這些解決方案的哪些方面會使它們更具吸引力?

+2

我的投票是#5。 [KISS](http://en.wikipedia.org/wiki/KISS_principle)。 – 2014-11-24 02:16:42

+0

想想#5。利弊。 mmap需要讀取磁盤,所以讀取。 Whay是一個騙局嗎? BTW +1 @Johnathon Reinhart – 2014-11-24 02:40:10

+0

字符串詳細信息:在C中,根據定義,字符串_always_具有終止''\ 0',否則它不是字符串。 'char'數組可能沒有''\ 0''。除了命名之外,不會改變你的問題。典型的文本文件沒有_any_字符串,但沒有文本行。 – chux 2014-11-24 03:10:40

回答

2

我建議在這裏進行範式轉換。

您正在查看由定義文本的'\ 0'分隔字符串組成的整個Universe。爲什麼不用這種方式來看待世界,爲什麼不試着看看文本被定義爲由開始和結束迭代器定義的序列的世界。

mmap文件,然後初步確定了開始迭代器,把它beg_iter到MMAP-ED段的開始,結束迭代器,調用它end_iter,第一個字節的mmap-的最後一個字節以下ED段或beg_iter+number_of_pages*pagesize,然後直到

A)end_iter等於beg_iter,或

B)beg_iter[-1]不是一個空字符,則

C)遞減end_iter,並返回步驟A.

完成後,您將有一對迭代器,開始迭代器值和定義您的文本字符串的結束迭代器值。

當然,在這種情況下,你的迭代器是普通的char *,但這真的不是很重要。重要的是,現在您發現自己擁有一套來自C++標準庫的豐富的算法和模板,可以讓您實施許多複雜的操作,既可變(如std::transform),也可以不可變(如std::find) 。

以空格結尾的字符串實際上是從plain C時代起的一種保留。對於C++,空字符結尾的字符串有些過時,並且很平常。現代C++代碼應該使用std::string對象,並使用開始和結束迭代器定義的序列。一個小的腳註:你可能會發現fstat()文件變得更加容易,並且獲得文件的精確長度(以字節爲單位),而不是mmap-它。那麼你現在完全知道得到了很多mmaped,並且你不必通過查看填充來反向工程。

+0

感謝您的回答。我真的想用C'str *()'函數尋找一種可用於C語言的解決方案,但基本上它聽起來像你所建議的類似於解決方案#1。關於'fstat()':絕對 - 我在我的例子中使用它。 – mattst88 2014-11-24 04:17:43

+0

關於你的答案的更多思考,我認爲你絕對是在存儲一個指向文本結尾的指針。這可以讓你簡單地計算你有多少剩餘的減法。有一個upvote! – mattst88 2014-11-24 06:47:42

相關問題