2012-05-06 6 views
6

आज की समस्या यह है कि मुझे प्रारंभिक स्थिति में बाइनरी फ़ाइल में संख्याओं की एक सरणी लिखनी होगी। मेरे पास वह स्थिति है जहां इसे शुरू करना चाहिए, और इसके बाद मैं मानों को ओवरराइट नहीं करना चाहता हूं, बस फ़ाइल में प्रारंभिक स्थिति में सरणी डालना चाहते हैं। उदाहरण के लिए:सी किसी भी मौजूदा सामग्री को ओवरराइट किए बिना बाइनरी फ़ाइल के बीच में लिखें

12345 

की स्थिति 2 में 456 पुश करते हैं:

12456345 

मुझे पता है कि शायद मैं अपने आप को द्वारा इसे लागू करना होगा, लेकिन मैं जानना चाहता हूँ कि क्या कैसे लागू करने के लिए पर अपनी राय है कि यथासंभव कुशलतापूर्वक।

+0

@ जॉन का जवाब ऐसा लगता है कि यह एकमात्र तरीका हो सकता है लेकिन इसमें बड़ी फ़ाइलों के लिए बहुत सारी प्रतिलिपि शामिल होगी। तो यदि संभव हो तो, डेटा क्रमबद्धता के लिए एक और दृष्टिकोण की तलाश करना सबसे अच्छा काम हो सकता है। – gcbenison

+0

@gcbenison, हाँ, मेरी बाइनरी फ़ाइल 1 जीबी हो सकती है और विस्तार प्रक्रिया बहुत समय तक ट्रिगर हो जाएगी, इसलिए यह शायद एक समस्या होगी –

+0

यदि आप फ़ाइल के बीच में डेटा डालने से बच सकते हैं तो यह सबसे अच्छा होगा, क्योंकि यह करने के लिए इतना महंगा है, गोगबाइट आकार फ़ाइलों में दोगुना है। –

उत्तर

9

यहाँ एक समारोह extend_file_and_insert() कि काम और अधिक या कम करता है, है।

#include <sys/stat.h> 
#include <unistd.h> 

enum { BUFFERSIZE = 64 * 1024 }; 

#define MIN(x, y) (((x) < (y)) ? (x) : (y)) 

/* 
off_t is signed 
ssize_t is signed 
size_t is unsigned 

off_t for lseek() offset and return 
size_t for read()/write() length 
ssize_t for read()/write() return 
off_t for st_size 
*/ 

static int extend_file_and_insert(int fd, off_t offset, char const *insert, size_t inslen) 
{ 
    char buffer[BUFFERSIZE]; 
    struct stat sb; 
    int rc = -1; 

    if (fstat(fd, &sb) == 0) 
    { 
     if (sb.st_size > offset) 
     { 
      /* Move data after offset up by inslen bytes */ 
      size_t bytes_to_move = sb.st_size - offset; 
      off_t read_end_offset = sb.st_size; 
      while (bytes_to_move != 0) 
      { 
       ssize_t bytes_this_time = MIN(BUFFERSIZE, bytes_to_move); 
       ssize_t rd_off = read_end_offset - bytes_this_time; 
       ssize_t wr_off = rd_off + inslen; 
       lseek(fd, rd_off, SEEK_SET); 
       if (read(fd, buffer, bytes_this_time) != bytes_this_time) 
        return -1; 
       lseek(fd, wr_off, SEEK_SET); 
       if (write(fd, buffer, bytes_this_time) != bytes_this_time) 
        return -1; 
       bytes_to_move -= bytes_this_time; 
       read_end_offset -= bytes_this_time; /* Added 2013-07-19 */ 
      } 
     } 
     lseek(fd, offset, SEEK_SET); 
     write(fd, insert, inslen); 
     rc = 0; 
    } 
    return rc; 
} 

(नोट अतिरिक्त लाइन 2013-07-19 जोड़ा गया;। यह एक बग केवल पता चलता है कि जब बफर आकार डेटा की मात्रा से छोटा होता है ओर इशारा करते हुए के लिए malat करने के लिए फ़ाइल धन्यवाद कॉपी करने के लिए था । त्रुटि बाहर संहिता अब BUFFERSIZE = 4 के साथ परीक्षण)

यह कुछ छोटे पैमाने पर परीक्षण कोड है:।

#include <fcntl.h> 
#include <string.h> 

static const char base_data[] = "12345"; 
typedef struct Data 
{ 
    off_t  posn; 
    const char *data; 
} Data; 
static const Data insert[] = 
{ 
    { 2, "456"      }, 
    { 4, "XxxxxxX"     }, 
    { 12, "ZzzzzzzzzzzzzzzzzzzzzzzzX" }, 
    { 22, "YyyyyyyyyyyyyyyY"   }, 
}; 
enum { NUM_INSERT = sizeof(insert)/sizeof(insert[0]) }; 

int main(void) 
{ 
    int fd = open("test.dat", O_RDWR | O_TRUNC | O_CREAT, 0644); 
    if (fd > 0) 
    { 
     ssize_t base_len = sizeof(base_data) - 1; 
     if (write(fd, base_data, base_len) == base_len) 
     { 
      for (int i = 0; i < NUM_INSERT; i++) 
      { 
       off_t length = strlen(insert[i].data); 
       if (extend_file_and_insert(fd, insert[i].posn, insert[i].data, length) != 0) 
        break; 
       lseek(fd, 0, SEEK_SET); 
       char buffer[BUFFERSIZE]; 
       ssize_t nbytes; 
       while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0) 
        write(1, buffer, nbytes); 
       write(1, "\n", 1); 
      } 
     } 
     close(fd); 
    } 
    return(0); 
} 

यह उत्पादन का उत्पादन:

12456345 
1245XxxxxxX6345 
1245XxxxxxX6ZzzzzzzzzzzzzzzzzzzzzzzzZ345 
1245XxxxxxX6ZzzzzzzzzzYyyyyyyyyyyyyyyYzzzzzzzzzzzzzzZ345 

यह कुछ बड़ी फ़ाइलों (परीक्षण के मुकाबले बड़े से अधिक) पर परीक्षण किया जाना चाहिए, लेकिन 64 कीबी से बहुत कम बुफे के साथ परीक्षण करना समझदारी होगी; मैंने 32 बाइट्स का इस्तेमाल किया और यह ठीक लग रहा था)। मैंने केवल परिणामों को नजरअंदाज कर दिया है लेकिन पैटर्न को यह देखने में आसान बनाने के लिए डिज़ाइन किया गया है कि वे सही हैं। कोड lseek() कॉलों में से कोई भी जांच नहीं करता है; यह एक मामूली जोखिम है।

+1

आपको read_end_offset – malat

+0

@malat अद्यतन करने की आवश्यकता है: हाँ, आप सही हैं। जब कोड को चलने वाले डेटा से एक से अधिक बफर-पूर्ण करने की आवश्यकता होती है, तो 'read_end_offset' के मान को 'bytes_this_time' द्वारा कम करने की आवश्यकता होती है। मैंने कोड 'BUFFERSIZE = 4' के साथ ठीक किया है और परीक्षण किया है (जो अवांछित कोड पर बग दिखाता है)। उस बग को इंगित करने के लिए धन्यवाद। –

5

सबसे पहले, फ़ाइल को अंतिम आकार में विस्तारित करने के लिए ftruncate() का उपयोग करें। फिर पुरानी छोर से लेकर नए सिरे तक सबकुछ कॉपी करें, सम्मिलन बिंदु पर वापस जाएं। फिर उस डेटा के साथ मध्य सामग्री को ओवरराइट करें जिसे आप सम्मिलित करना चाहते हैं। यह जितना कुशल हो जाता है, मुझे लगता है, क्योंकि फाइल सिस्टम आमतौर पर फ़ाइलों के बीच में "सम्मिलन" प्रदान नहीं करते हैं।

+0

यह एक अच्छा समाधान है। मुझे पता है कि यह एक बेवकूफ सवाल है, लेकिन क्या आप कृपया एक उदाहरण या एक अच्छा लिंक प्रदान कर सकते हैं? धन्यवाद दोस्त! –

+3

@FredericoSchardong: बस इसे लिखें। एक या दो चीज़ सीखो। –

0

मैं आपके प्रश्न को व्यापक रूप से समझने जा रहा हूं कि "मैं किसी ऑब्जेक्ट की कुशलतापूर्वक एक सतत स्टोर कैसे कार्यान्वित कर सकता हूं जो इंडेक्स द्वारा यादृच्छिक-पहुंच लुकअप का समर्थन करता है और विस्तार के साथ सम्मिलन का समर्थन करता है।" जैसा कि ध्यान दिया गया है, आप फ़ाइल में एक साधारण रैखिक सरणी का उपयोग कर सकते हैं, लेकिन यह केवल लुकअप (ओ (1)) के लिए कुशल होगा और सम्मिलन (ओ (एन)) के लिए काफी अक्षम होगा। आप इसके बजाय पेड़ डेटा संरचना का उपयोग कर लुकअप और सम्मिलन दोनों के लिए ओ (लॉग एन) प्राप्त कर सकते हैं। एक फ़ाइल को बनाए रखें जो एक इंडेक्स के रूप में कार्य करता है, और दूसरा जो डेटा स्टोर के रूप में कार्य करता है और भाग की एक श्रृंखला है। प्रत्येक खंड आंशिक रूप से भरा जा सकता है। इंडेक्स फ़ाइल में एक पेड़ (बाइनरी पेड़ या बी-पेड़) होता है जहां प्रत्येक नोड सरणी के कुछ संगत खंड से मेल खाता है और उस खंड का आकार होता है (ताकि रूट नोड में पूरे सरणी का आकार हो)। बाइनरी पेड़ के लिए, बाएं और दाएं बच्चे नोड्स में सरणी के बाएं और दाएं हिस्सों (लगभग) का आकार होता है। अंत में पत्ती नोड्स में डेटा स्टोर फ़ाइल में एक खंड में पॉइंटर होता है जिसमें वास्तविक डेटा होता है। सम्मिलन में अब 'के' नोड्स की 'आकार' संपत्ति को बदलना शामिल है, जहां 'के' पेड़ की ऊंचाई है। जब एक डेटा स्टोर खंड बहुत भरा हो जाता है, तो इसे विभाजित करें (फ़ाइल को बढ़कर एक नया स्थान आवंटित करें, या यदि आप हटाए जाने का समर्थन करते हैं, शायद खाली हिस्से की मुफ्त सूची से) और पेड़ को पुनर्व्यवस्थित करें (करने के कई मानक तरीके यह।)

क्या यह ध्वनि जटिल है? निश्चित रूप से! कुशल मध्य-फ़ाइल सम्मिलन संलग्न होने से प्राप्त करने के लिए अधिक जटिल है।

+0

जैसा कि मेरी फाइलें 1 जीबी तक बढ़ सकती हैं, मुझे हजारों मध्य सम्मिलन की उम्मीद है। मैं रूज फ़ाइल को छोटे से विभाजित करने के बारे में सोच रहा था जिसमें मैं उनके अंत में नई सामग्री को जोड़ सकता हूं, फिर जब सब कुछ किया जाता है तो बस छोटी फाइलों में शामिल हों।मेरा मानना ​​है कि परिचालन संचालन के माध्यम से छोटी फाइलों का उपयोग करने से कहीं अधिक कुशल नहीं है। @gcbenison आपको क्या लगता है? –

+0

आप बड़ी फ़ाइलों में छोटे टुकड़ों की बजाय छोटी फ़ाइलों का एक गुच्छा रख सकते हैं, लेकिन आपको अभी भी भाग को अनुक्रमणित करने के कुछ तरीके की आवश्यकता है। यदि इस तरह से प्रत्येक पर एक अनुक्रमिक लेबल चिपकाना है, तो सम्मिलन ओ (एन) बन जाता है क्योंकि आपको प्रत्येक के लेबल को अपडेट करना होता है। इसलिए पेड़ आधारित दृष्टिकोण। – gcbenison

+0

प्रत्येक के लेबल को अपडेट करें? ऐसा करने की कोई आवश्यकता नहीं है। –

0

मैं दूसरों के साथ सहमत हो रही है, लेकिन मुझे समाधान थोड़ा अलग राज्य करते हैं:

  1. एक अस्थायी फ़ाइल नाम प्राप्त

  2. कॉपी (इसके लिए ओएस-विशिष्ट कॉल कर रहे हैं) अपने अस्थायी फ़ाइल में मूल फ़ाइल (अब एक ही फ़ाइल की दो प्रतियां हैं)

  3. "संलग्न" के लिए मूल फ़ाइल खोलें।

  4. अपने सम्मिलन बिंदु को यह "काटें"

  5. अपने नए डेटा

  6. सम्मिलन बिंदु करने के लिए "शोध" (फिर से "पढ़ें" के लिए अपने अस्थायी फ़ाइल को खोलने लिखें, कॉल ओएस-विशिष्ट है)

  7. temp फ़ाइल में फ़ाइल के अंत में पढ़ें; अपनी मूल फ़ाइल में डालना (अभी भी "संलग्न" के लिए खुला है)।

  8. बंद दोनों फ़ाइलों

  9. हटाएँ अस्थायी फ़ाइल

+0

आपके उत्तर के लिए धन्यवाद, बहुत बढ़िया लग रहा है। मैं वास्तव में छोटी फाइलों के विचार में संलग्न हूं क्योंकि यह उस तरीके से होता है जिसकी कम मात्रा में I/O है क्योंकि प्रारंभ में प्रस्तावित प्रत्येक "मध्य सम्मिलन" के लिए केवल एक परिशिष्ट ऑपरेशन होगा। आपके सभी 10 चरणों के बजाय मेरे पास सिर्फ एक ही संलग्न होगा। क्या आप @ paulsm4 से सहमत हैं? –

संबंधित मुद्दे

 संबंधित मुद्दे