2013-01-10 46 views
5

मैं जावा से सी से कुछ कोड पोर्ट कर रहा हूं, और अब तक चीजें अच्छी तरह से चली गई हैं।सी में स्ट्रिंग concatenation के बहुत सारे प्रदर्शन?

हालांकि, मैं इस तरह कि StringBuilder के उदारवादी उपयोग करता है जावा में एक विशेष समारोह है,:

StringBuilder result = new StringBuilder(); 
// .. build string out of variable-length data 
for (SolObject object : this) { 
    result.append(object.toString()); 
} 
// .. some parts are conditional 
if (freezeCount < 0) result.append("]"); 
else result.append(")"); 

मैं इतना एहसास एक कोड अनुवाद सेवा नहीं है, लेकिन मैं किसी का अनुवाद करने के लिए नहीं पूछ रहा हूँ उपरोक्त कोड।

मैं सोच रहा हूं कि इस प्रकार के द्रव्यमान स्ट्रिंग कॉन्सटेनेशन को सी में कुशलतापूर्वक कैसे किया जाए। यह ज्यादातर छोटे तार हैं, लेकिन प्रत्येक को एक शर्त द्वारा निर्धारित किया जाता है, इसलिए मैं उन्हें एक सरल sprintf कॉल में संयोजित नहीं कर सकता।

मैं इस तरह के स्ट्रिंग कॉन्सटेनेशन को विश्वसनीय रूप से कैसे कर सकता हूं?

+0

आप सख्ती से सी का उपयोग कर रहे

यहां नमूने के परीक्षण समारोह है? या आप सी ++ का भी उपयोग कर सकते हैं? – Nico

+2

ठीक है आप उन्हें sprintf() कॉल के समूह में जोड़ सकते हैं। बस वापसी मूल्य का उपयोग करें ... – wildplasser

+0

@ निको मैं सादा सी का उपयोग कर रहा हूं, और मैं सी ++ से बचना चाहता हूं। –

उत्तर

4

स्ट्रिंग के लिए "वस्तुओं" के एक नंबर बातचीत करने के लिए एक नहीं बल्कि "चालाक" तरीका है:

char buffer[100]; 
char *str = buffer; 
str += sprintf(str, "%06d", 123); 
str += sprintf(str, "%s=%5.2f", "x", 1.234567); 

के बाद से sprintf स्ट्रिंग की नकल की की लंबाई देता है यह काफी कुशल है, इसलिए हम कर सकते हैं "चाल" वापसी मूल्य से आगे बढ़ें, और 0 भरें।

बेशक, यदि वास्तविक जावा ऑब्जेक्ट्स हैं, तो आपको यह पता लगाना होगा कि जावा स्टाइल ToString फ़ंक्शन को सी के printf में "% somethign" में कैसे बनाना है परिवार।

+0

बस सुनिश्चित करें कि आप जो भी हो 'sprintf'-ing 99 वर्णों से अधिक नहीं है (टर्मिनल नल कैरेक्टर के लिए एक 'char' छोड़ें) या आपको एक बफर ओवररन और शायद, एक सेगमेंटेशन गलती मिल जाएगी। –

+0

मैंने इसे चेक के साथ कर दिया बफर ओवररन (अगर यह करता है तो 'reallocf' के साथ)। मुझे लगता है कि अगर यह एक समस्या होने पर समाप्त होता है तो मैं इसे और अधिक अनुकूलित कर दूंगा, लेकिन अभी के लिए, यह वास्तव में सुरुचिपूर्ण दिखता है! –

0

यह देखते हुए कि तार बहुत छोटे लगते हैं, मैं केवल strcat का उपयोग करने के इच्छुक हूं और यदि प्रदर्शन एक मुद्दा बन जाता है तो पुनरीक्षण करें।

आप अपनी खुद की विधि बना सकते हैं जो स्ट्रिंग लम्बाई को याद रखता है, इसलिए अंत को खोजने के लिए स्ट्रिंग के माध्यम से इसे फिर से चलाने की आवश्यकता नहीं होती है (यदि आप लंबी स्ट्रिंग्स में बहुत सारे एपेंड कर रहे हैं तो संभावित रूप से स्ट्रैट की धीमी बिट है)

2

strcat() के साथ प्रदर्शन समस्या यह है कि इसे \0' को समाप्ति शुरू करने से पहले इसे गंतव्य स्ट्रिंग को स्कैन करना होगा।

लेकिन याद रखें कि strcat()तार नहीं ले करता है तर्कों के रूप में, यह संकेत लेता है।

तो आपको एक अलग सूचक कि हमेशा स्ट्रिंग आप के लिए जोड़ रहे हैं के समाप्त '\0' के लिए अंक बनाए रखने, तो आप उस सूचक पहले तर्क के रूप में strcat() का उपयोग कर सकते हैं, और यह फिर से स्कैन करने के लिए यह हर ज़रूरत नहीं होगी पहर। उस मामले के लिए, आप strcat() से राटर का उपयोग कर सकते हैं।

इस सूचक के मूल्य को बनाए रखना और यह सुनिश्चित करना कि अभ्यास के रूप में पर्याप्त जगह छोड़ी गई हो।

नोट: आप गंतव्य सरणी के अंत को ओवरराइट करने से बचने के लिए strncat() का उपयोग कर सकते हैं (हालांकि यह आपके डेटा को चुपचाप छोटा कर देगा)। मैं इस उद्देश्य के लिए strncpy() का उपयोग करने की अनुशंसा नहीं करता हूं। my rant on the subject देखें।

यदि आपका सिस्टम उनका समर्थन करता है, तो (गैर-मानक) strcpy() और strlcat() फ़ंक्शन इस तरह की चीज़ के लिए उपयोगी हो सकते हैं। वे दोनों स्ट्रिंग की कुल लंबाई को वापस करने की कोशिश करते हैं। लेकिन उनका उपयोग आपके कोड को कम पोर्टेबल बनाता है; दूसरी ओर, खुले स्रोत कार्यान्वयन हैं जिनका आप कहीं भी उपयोग कर सकते हैं।

एक अन्य समाधान strlen() को उस स्ट्रिंग पर कॉल करना है जिसे आप जोड़ रहे हैं। यह आदर्श नहीं है, क्योंकि इसे दो बार स्कैन किया जाता है, एक बार strcat() और एक बार strlen() - लेकिन कम से कम यह पूरे गंतव्य स्ट्रिंग को फिर से स्कैन करने से बचाता है।

+1

"इस सूचक के मूल्य को बनाए रखना और यह सुनिश्चित करना कि अभ्यास के रूप में पर्याप्त जगह छोड़ी जा सके।"लेकिन ऐसा लगता है कि मानक स्ट्रिंग फ़ंक्शंस के साथ ऐसा करने का कोई अच्छा तरीका नहीं है। दुर्भाग्य से मानक स्ट्रिंग फ़ंक्शंस '\ 0' को समाप्त करने के लिए पॉइंटर वापस नहीं करते हैं, लेकिन स्ट्रिंग की शुरुआत में। अन्य स्ट्रिंग लाइब्रेरीज़ की आवश्यकता होती है इस सीमा को हल करने के लिए, मुझे लगता है। –

+0

आप हमेशा आने वाले तारों पर स्ट्रेल चला सकते हैं और किसी अन्य स्ट्रेलन कॉल से बचने के लिए कार्यों के सेट * सेट का उपयोग कर सकते हैं। (स्मृति और स्ट्रिंग पूंछ का ट्रैक रखने के लिए) – Eugene

1

यदि इन तरह के संचालन बहुत बार होते हैं, तो आप उन्हें अपने स्वयं के बफर कक्षा में लागू कर सकते हैं। उदाहरण (त्रुटि हैंडलिंग संक्षिप्तता के लिए ;-) छोड़े गए:

struct buff { 
     size_t used; 
     size_t size; 
     char *data; 
     } ; 

struct buff * buff_new(size_t size) 
{ 
struct buff *bp; 
bp = malloc (sizeof *bp); 
bp->data = malloc (size); 
bp->size = size; 
bp->used = 0; 
return bp; 
} 

void buff_add_str(struct buff *bp, char *add) 
{ 
size_t len; 
len = strlen(add); 

     /* To be implemented: buff_resize() ... */ 
if (bp->used + len +1 >= bp->size) buff_resize(bp, bp->used+1+len); 

memcpy(buff->data + buff->used, add, len+1); 

buff->used += len; 
return; 
} 
2

खराब प्रदर्शन के कारण जब तार श्रृंखलाबद्ध स्मृति के पुनः आबंटन है। जोएल स्पॉस्की ने अपने लेख Back to basics में इस पर चर्चा की। उन्होंने श्रृंखलाबद्ध तार के अनुभवहीन विधि का वर्णन करता है:

Shlemiel में एक सड़क का चित्रकार की नौकरी मिल जाती, सड़क के बीच नीचे बिंदु वाली रेखा चित्र। पहले दिन वह सड़क पर पेंट करने का एक तरीका लेता है और सड़क के 300 गज की दूरी तय करता है। "वह बहुत बढिया है!" अपने मालिक कहते हैं, "आप एक तेज कार्यकर्ता हैं!" और उसे एक कोपेक देता है।

अगले दिन शलेमिल केवल 150 गज की दूरी पर हो जाता है। "ठीक है, यह कल के रूप में लगभग उतना अच्छा नहीं है, लेकिन आप अभी भी एक तेज कार्यकर्ता हैं। 150 गज सम्मानजनक है," और उसे एक कोपेक देता है।

अगले दिन शलेमेल सड़क के 30 गज की दूरी पर चित्रित करता है। "केवल 30!" अपने मालिक को चिल्लाता है। "यह अस्वीकार्य है! पहले दिन आपने दस गुना इतना काम किया था! क्या चल रहा है?" श्लेमीएल कहते हैं,

"मैं इसकी मदद नहीं कर सकता"। "हर दिन मैं पेंट से दूर और दूर हो सकता है!"

यदि आप कर सकते हैं, तो आप जानना चाहते हैं कि आपके गंतव्य बफर को आवंटित करने से पहले कितना बड़ा होना चाहिए। ऐसा करने का एकमात्र यथार्थवादी तरीका उन सभी तारों पर strlen पर कॉल करना है जिन्हें आप जोड़ना चाहते हैं। फिर उचित मात्रा में स्मृति आवंटित करें और strncpy के थोड़ा संशोधित संस्करण का उपयोग करें जो गंतव्य बफर के अंत में पॉइंटर लौटाता है।

// Copies src to dest and returns a pointer to the next available 
// character in the dest buffer. 
// Ensures that a null terminator is at the end of dest. If 
// src is larger than size then size - 1 bytes are copied 
char* StringCopyEnd(char* dest, char* src, size_t size) 
{ 
    size_t pos = 0; 
    if (size == 0) return dest; 

    while (pos < size - 1 && *src) 
    { 
     *dest = *src; 
     ++dest; 
     ++src; 
     ++pos; 
    } 
    *dest = '\0'; 
    return dest; 
} 

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

void testStringCopyEnd(char* str1, char* str2, size_t size) 
{ 
    // Create an oversized buffer and fill it with A's so that 
    // if a string is not null terminated it will be obvious. 
    char* dest = (char*) malloc(size + 10); 
    memset(dest, 'A', size + 10); 
    char* end = StringCopyEnd(dest, str1, size); 
    end = StringCopyEnd(end, str2, size - (end - dest)); 
    printf("length: %d - '%s'\n", strlen(dest), dest); 
} 

int main(int argc, _TCHAR* argv[]) 
{ 
    // Test with a large enough buffer size to concatenate 'Hello World'. 
    // and then reduce the buffer size from there 
    for (int i = 12; i > 0; --i) 
    { 
     testStringCopyEnd("Hello", " World", i); 
    } 
    return 0; 
} 

कौन सा पैदा करता है:

length: 11 - 'Hello World' 
length: 10 - 'Hello Worl' 
length: 9 - 'Hello Wor' 
length: 8 - 'Hello Wo' 
length: 7 - 'Hello W' 
length: 6 - 'Hello ' 
length: 5 - 'Hello' 
length: 4 - 'Hell' 
length: 3 - 'Hel' 
length: 2 - 'He' 
length: 1 - 'H' 
length: 0 - '' 
+0

नोट: यह एक terribele जवाब है (बहुत अधिक तुलनीय डरावने strncpy()) :: यह ** हमेशा ** लक्ष्य स्ट्रिंग unterminated छोड़ देंगे। – wildplasser

+0

@ विल्डप्लेसर मैंने एक टिप्पणी जोड़ दी है कि यह कभी भी स्ट्रिंग को समाप्त नहीं करेगा और यह सुनिश्चित करेगा कि कॉलिंग कोड सुनिश्चित करेगा कि एक शून्य टर्मिनेटर होगा। – Steve

+1

यह अभी भी भयानक है, IMnsvHO। आप केवल नाम छोड़ने के माध्यम से उत्पन्न होते हैं (बीटीडब्ल्यू जोएल स्पॉल्स्की के पास सी ++ उच्चारण है, इसलिए आप अच्छी कंपनी में हैं) एनआईएल-टर्मिनल को कॉलर के लिए एक कार्य के रूप में रखना एपीआई-डिज़ाइन के मामले में एक बहुत ही बुरी आदत है। तार तार हैं। इसके साथ सौदा) नोट: मैं डाउनवोट नहीं करता हूं। मैं कभी नही करता हूँ। जो लोग इस पर विश्वास करते हैं, हारने के लायक हैं, आईएमएचओ। – wildplasser