সি ফর্ম্যাট করা স্ট্রিং তৈরি করা হচ্ছে (সেগুলি মুদ্রণ করছে না)


105

আমার একটি ফাংশন রয়েছে যা একটি স্ট্রিং গ্রহণ করে, এটি হল:

void log_out(char *);

এটিকে কল করার সময়, আমাকে ফ্লাইতে ফর্ম্যাট করা স্ট্রিং তৈরি করতে হবে যেমন:

int i = 1;
log_out("some text %d", i);

আমি কীভাবে এএনএসআই সি তে এটি করব?


কেবলমাত্র যেহেতু sprintf()কোনও int প্রদান করে, এর অর্থ হ'ল আমাকে কমপক্ষে 3 টি কমান্ড লিখতে হবে, যেমন:

char *s;
sprintf(s, "%d\t%d", ix, iy);
log_out(s);

এটিকে ছোট করার কোনও উপায়?


4
আমি বিশ্বাস করি যে ফাংশন প্রোটোটাইপটি সত্যই: বহিরাগত শূন্য লগ_আউট (কনস্ট চর *, ...); কারণ যদি তা না হয় তবে এটির কলটি ভুল (খুব বেশি যুক্তি দেওয়া)। এটি কনস্ট পয়েন্টার গ্রহণ করা উচিত কারণ স্ট্রিংটি সংশোধন করার জন্য লগ_আউট () করার কোনও কারণ নেই। অবশ্যই, আপনি বলছেন যে আপনি ফাংশনটিতে একটি স্ট্রিং পাস করতে চান - তবে পারবেন না। তারপরে একটি বিকল্প হ'ল লগ_আউট () ফাংশনের ভারার্গ্স সংস্করণ লিখুন।
জোনাথন লেফলার

উত্তর:


94

স্প্রিন্টফ ব্যবহার করুন ।

int sprintf ( char * str, const char * format, ... );

স্ট্রিংয়ে ফর্ম্যাট করা ডেটা লিখুন একই পাঠ্যের সাথে একটি স্ট্রিং রচনা করে যা প্রিন্টফে ফর্ম্যাটটি ব্যবহার করা হত তবে মুদ্রিত হওয়ার পরিবর্তে সামগ্রীটি স্ট্রিং দ্বারা চিহ্নিত বাফারে একটি সি স্ট্রিং হিসাবে সংরক্ষণ করা হয়।

পুরো ফলাফলের স্ট্রিংটি ধারণ করতে বাফারের আকার যথেষ্ট পরিমাণে বড় হওয়া উচিত (নিরাপদ সংস্করণের জন্য স্নিপ্রিন্টফ দেখুন)।

একটি সমাপ্ত নাল অক্ষর স্বয়ংক্রিয়ভাবে সামগ্রী পরে যুক্ত করা হয়।

ফর্ম্যাট প্যারামিটার পরে, ফাংশনটি বিন্যাসের জন্য কমপক্ষে অতিরিক্ত অতিরিক্ত যুক্তিগুলি প্রত্যাশা করে।

পরামিতি:

str

বাফারের পয়েন্টার যেখানে ফলাফলের সি-স্ট্রিং সঞ্চিত থাকে। ফলাফলের স্ট্রিংটি ধারণ করতে বাফারটি যথেষ্ট পরিমাণে বড় হওয়া উচিত।

format

সি স্ট্রিংয়ে এমন ফর্ম্যাট স্ট্রিং রয়েছে যা প্রিন্টফের বিন্যাসের মতো একই স্পেসিফিকেশনগুলি অনুসরণ করে (বিশদগুলির জন্য প্রিন্টফ দেখুন)।

... (additional arguments)

ফর্ম্যাট স্ট্রিংয়ের উপর নির্ভর করে ফাংশনটি অতিরিক্ত আর্গুমেন্টের অনুক্রমের আশা করতে পারে, প্রতিটি ফর্ম্যাট স্ট্রিংয়ের বিন্যাস নির্দিষ্টকরণকারক (বা n এর জন্য একটি স্টোরেজ অবস্থানে একটি পয়েন্টার) প্রতিস্থাপনের জন্য ব্যবহৃত একটি মান রয়েছে। ফর্ম্যাট স্পেসিফায়ারগুলিতে নির্দিষ্ট মানগুলির সংখ্যা হিসাবে কমপক্ষে এই যুক্তি থাকা উচিত। অতিরিক্ত যুক্তি ফাংশন দ্বারা উপেক্ষা করা হয়।

উদাহরণ:

// Allocates storage
char *hello_world = (char*)malloc(13 * sizeof(char));
// Prints "Hello world!" on hello_world
sprintf(hello_world, "%s %s!", "Hello", "world");

35
হায়! যদি সম্ভব হয় তবে 'এন' ভেরিয়েশন ফাংশন ব্যবহার করুন। অর্থাত্ স্নিপ্রিন্টফ। তারা আপনাকে আপনার বাফার আকারগুলি গণনা করতে এবং ততক্ষণ অতিরিক্ত পরিমাণের বিরুদ্ধে বীমা করতে বাধ্য করবে।
dmckee --- প্রাক্তন-মডারেটর বিড়ালছানা

7
হ্যাঁ, তবে তিনি একটি এএনএসআই সি ফাংশন চেয়েছিলেন এবং আমি এতটা নিশ্চিত নই যে স্নিপ্রিন্টফ আনসি বা এমনকি পিক্সিক্স কিনা।
আর্প্পা

7
আহ। আমি সেটা মিস করেছি. তবে আমি নতুন স্ট্যান্ডার্ড দ্বারা উদ্ধার পেয়েছি: 'এন' রূপগুলি C99 তে অফিসিয়াল।
এফডাব্লুআইডাব্লু

4
স্নিপ্রিন্টফ সবচেয়ে নিরাপদ উপায় নয়। আপনার স্নিপ্রিন্টফ_স নিয়ে যাওয়া উচিত দেখুন msdn.microsoft.com/en-us/library/f30dzcf6(VS.80).aspx
joce

4
@ জোস - সি 99 সানপ্রিন্টফ () ফাংশনগুলির পরিবারটি বেশ সুরক্ষিত; তবে স্নিপ্রিন্টফ_স () পরিবারের সোয়েমের বিভিন্ন আচরণ রয়েছে (বিশেষত ট্রানশানটি কীভাবে পরিচালনা করা হয় সে সম্পর্কে)। কিন্তু - মাইক্রোসফ্টের _স্নিপ্রিন্টফ () ফাংশনটি নিরাপদ নয় - কারণ এটি সম্ভবত ফলস্বরূপ বাফারকে নিরবচ্ছিন্নভাবে ছেড়ে দিতে পারে (সি 99 স্নিপ্রিন্টফ () সর্বদা শেষ করে)।
মাইকেল বুড়

17

আপনার যদি পসিক্স -2008 কমপ্লায়েন্ট সিস্টেম (যে কোনও আধুনিক লিনাক্স) থাকে তবে আপনি নিরাপদ এবং সুবিধাজনক asprintf()ফাংশনটি ব্যবহার করতে পারেন : এটি আপনার malloc()পক্ষে যথেষ্ট মেমরির হবে , আপনার সর্বোচ্চ স্ট্রিংয়ের আকার সম্পর্কে চিন্তা করার দরকার নেই। এটি এর মতো ব্যবহার করুন:

char* string;
if(0 > asprintf(&string, "Formatting a number: %d\n", 42)) return error;
log_out(string);
free(string);

সুরক্ষিত ফ্যাশনে স্ট্রিংটি তৈরি করতে আপনি পেতে পারেন এটি সর্বনিম্ন প্রচেষ্টা। sprintf()আপনি প্রশ্নের যে কোড দিয়েছেন তা গভীরভাবে ত্রুটিযুক্ত:

  • পয়েন্টারের পিছনে কোনও বরাদ্দ মেমরি নেই। আপনি স্মৃতিতে একটি এলোমেলো স্থানে স্ট্রিং লিখছেন!

  • এমনকি যদি আপনি লিখেছিলেন

    char s[42];
    

    আপনি গভীর সমস্যায় পড়বেন, কারণ বন্ধনীতে কোন সংখ্যাটি রাখতে হবে তা আপনি জানেন না।

  • এমনকি যদি আপনি "নিরাপদ" বৈকল্পিক ব্যবহার করেছিলেন snprintf(), আপনি এখনও আপনার স্ট্রিং কেটে ফেলার ঝুঁকিটি চালিয়ে যাবেন। লগ ফাইলে লেখার সময়, এটি তুলনামূলকভাবে সামান্য উদ্বেগ, তবে এটি কার্যকরভাবে কার্যকর তথ্যটি কাটানোর সম্ভাবনা রাখে। এছাড়াও, এটি আপনার ব্যর্থ লিখিত লাইনের শেষে পরবর্তী লগ লাইনটি গ্লুয়িং করে শেষের অক্ষরটি শেষ করে দেবে।

  • আপনি যদি সব ক্ষেত্রে একটি সংমিশ্রণ ব্যবহার করে malloc()এবং snprintf()সঠিক আচরণ তৈরি করার চেষ্টা করেন তবে আপনি যে পরিমাণ কোড দিয়েছেন তার চেয়ে দ্বিগুণ কোড দিয়ে শেষ করবেন asprintf()এবং মূলত এর কার্যকারিতা পুনরায় প্রোগ্রাম করুন asprintf()


আপনি যদি এর একটি মোড়ক সরবরাহের দিকে তাকিয়ে থাকেন তবে স্টাইলের প্যারামিটারের তালিকা নিজেই log_out()নিতে পারেন printf(), আপনি vasprintf()একটি রূপটি যা va_listযুক্তি হিসাবে গ্রহণ করবে তা ব্যবহার করতে পারেন । এই জাতীয় একটি মোড়কের পুরোপুরি নিরাপদ বাস্তবায়ন:

//Tell gcc that we are defining a printf-style function so that it can do type checking.
//Obviously, this should go into a header.
void log_out_wrapper(const char *format, ...) __attribute__ ((format (printf, 1, 2)));

void log_out_wrapper(const char *format, ...) {
    char* string;
    va_list args;

    va_start(args, format);
    if(0 > vasprintf(&string, format, args)) string = NULL;    //this is for logging, so failed allocation is not fatal
    va_end(args);

    if(string) {
        log_out(string);
        free(string);
    } else {
        log_out("Error while logging a message: Memory allocation failed.\n");
    }
}

4
দ্রষ্টব্য এটি asprintf()স্ট্যান্ডার্ড সি ২০১১ এর অংশ বা পসিক্সের অংশ নয়, এমনকি পসিক্স ২০০৮ বা 2013 নয় not এটি টিআর 27431-2 এর অংশ: দেখুন আপনি কি টিআর 24731 'নিরাপদ' ফাংশন ব্যবহার করেন?
জোনাথন লেফলার 24'14

কবজ মত কাজ! আমি ইস্যুটির মুখোমুখি হয়েছি, যখন "চর *" মান লগগুলিতে সঠিকভাবে মুদ্রিত হচ্ছে না, অর্থাৎ ফর্ম্যাট স্ট্রিংটি কোনওভাবেই উপযুক্ত ছিল না। কোডটি ব্যবহার করছিল, "asprintf ()"।
প্যারাসিশ

12

আমার কাছে মনে হচ্ছে আপনি ইতিমধ্যে যে সাধারণ স্ট্রিং নিয়েছেন তার ফাংশনে প্রিন্টফ-স্টাইল বিন্যাস ব্যবহার করে তৈরি স্ট্রিংটি সহজেই পাস করতে সক্ষম হতে চান। আপনি stdarg.hসুবিধাগুলি এবং vsnprintf()(যা আপনার সংকলক / প্ল্যাটফর্মের উপর নির্ভর করে সহজেই উপলভ্য নাও হতে পারে) ব্যবহার করে একটি মোড়ক ফাংশন তৈরি করতে পারেন :

#include <stdarg.h>
#include <stdio.h>

// a function that accepts a string:

void foo( char* s);

// You'd like to call a function that takes a format string 
//  and then calls foo():

void foofmt( char* fmt, ...)
{
    char buf[100];     // this should really be sized appropriately
                       // possibly in response to a call to vsnprintf()
    va_list vl;
    va_start(vl, fmt);

    vsnprintf( buf, sizeof( buf), fmt, vl);

    va_end( vl);

    foo( buf);
}



int main()
{
    int val = 42;

    foofmt( "Some value: %d\n", val);
    return 0;
}

snprintf()রুটিনের পরিবারের কোনও ভাল বাস্তবায়ন (বা কোনও বাস্তবায়ন) সরবরাহ না করে এমন প্ল্যাটফর্মগুলির জন্য , আমি সফলভাবে হোলার ওয়েইসের কাছ থেকে প্রায় একটি পাবলিক ডোমেনsnprintf() ব্যবহার করেছি ।


বর্তমানে, কেউ vsnprintf_s ব্যবহার বিবেচনা করতে পারে।
একত্রে

3

আপনার যদি কোডটি থাকে তবে log_out()এটি আবার লিখুন। সম্ভবত, আপনি এটি করতে পারেন:

static FILE *logfp = ...;

void log_out(const char *fmt, ...)
{
    va_list args;

    va_start(args, fmt);
    vfprintf(logfp, fmt, args);
    va_end(args);
}

অতিরিক্ত লগিংয়ের তথ্য যদি প্রয়োজন হয় তবে তা বার্তাটি দেখানোর আগে বা পরে মুদ্রণ করা যেতে পারে। এটি মেমরি বরাদ্দ এবং সন্দেহজনক বাফার আকার এবং আরও অনেক কিছু সংরক্ষণ করে। আপনার সম্ভবত logfpশূন্যের (নাল পয়েন্টার) আরম্ভ করতে হবে এবং এটি নাল কিনা তা যাচাই করা উচিত এবং যথাযথ হিসাবে লগ ফাইলটি খুলুন - তবে বিদ্যমান কোডটি log_out()যেভাবেই হোক না কেন এটির সাথে আচরণ করা উচিত।

এই সমাধানটির সুবিধাটি হ'ল আপনি কেবল এটিকে কল করতে পারেন যেন এটির কোনও রূপ printf(); আসলে এটি একটি ছোটখাটো রূপ printf()

যদি আপনার কাছে কোডটি না থাকে তবে log_out()আপনি এটি উপরে বর্ণিত একটি যেমন বৈকল্পিক দিয়ে প্রতিস্থাপন করতে পারেন কিনা তা বিবেচনা করুন। আপনি একই নামটি ব্যবহার করতে পারবেন কিনা তা আপনার অ্যাপ্লিকেশন কাঠামো এবং বর্তমান log_out()ফাংশনের চূড়ান্ত উত্সের উপর নির্ভর করবে । এটি যদি অন্য অপরিহার্য ফাংশন হিসাবে একই অবজেক্ট ফাইলে থাকে তবে আপনাকে একটি নতুন নাম ব্যবহার করতে হবে। আপনি কীভাবে এটির সঠিক প্রতিলিপি তৈরি করতে সক্ষম না করতে পারেন, আপনাকে অন্যান্য উত্তরে দেওয়া মত কিছু বৈকল্পিক ব্যবহার করতে হবে যা যথাযথ পরিমাণের মেমরি বরাদ্দ করে।

void log_out_wrapper(const char *fmt, ...)
{
    va_list args;
    size_t  len;
    char   *space;

    va_start(args, fmt);
    len = vsnprintf(0, 0, fmt, args);
    va_end(args);
    if ((space = malloc(len + 1)) != 0)
    {
         va_start(args, fmt);
         vsnprintf(space, len+1, fmt, args);
         va_end(args);
         log_out(space);
         free(space);
    }
    /* else - what to do if memory allocation fails? */
}

স্পষ্টতই, আপনি এখন এর log_out_wrapper()পরিবর্তে কল করুন log_out()- তবে মেমরির বরাদ্দ এবং আরও একবার একবার হয়ে গেছে। আমি একটি অপ্রয়োজনীয় বাইট দ্বারা অতিরিক্ত বরাদ্দ পাওয়ার অধিকার সংরক্ষণ করি - আমি যে দৈর্ঘ্যটি ফিরিয়ে দিয়েছি vsnprintf()তাতে সমাপ্ত নাল অন্তর্ভুক্ত কিনা তা আমি ডাবল-চেক করে দেখিনি ।


3

স্প্রিন্টফ ব্যবহার করবেন না।
এটি আপনার স্ট্রিং-বাফারকে উপচে ফেলেছে এবং আপনার প্রোগ্রামটি ক্র্যাশ করবে।
সর্বদা স্নিপ্রিন্টফ ব্যবহার করুন


0

আমি এটি করিনি, সুতরাং আমি ঠিক সঠিক উত্তরটি নির্দেশ করতে যাচ্ছি।

সি এর <stdarg.h>শিরোনাম ব্যবহার করে এমন ফাংশনগুলির বিধান রয়েছে যা অপ্রয়োজনীয় সংখ্যক ক্রিয়াকলাপ গ্রহণ করে । আপনি আপনার ফাংশনটিকে হিসাবে সংজ্ঞায়িত করতে পারেন void log_out(const char *fmt, ...);এবং va_listফাংশনটির অভ্যন্তরটি পেতে পারেন । তারপরে আপনি মেমরি বরাদ্দ করতে এবং vsprintf()বরাদ্দ হওয়া মেমরি, ফর্ম্যাট এবং দিয়ে কল করতে পারেন va_list

পর্যায়ক্রমে, আপনি এটিকে sprintf()মেমরির বরাদ্দ করতে এবং ফর্ম্যাট করা স্ট্রিংটি ফিরে আসার জন্য অনুরূপ একটি ফাংশন লিখতে ব্যবহার করতে পারেন , এটি উপরের মতো কমবেশি উত্পন্ন করে। এটি একটি মেমরি ফাঁস হবে, তবে আপনি যদি কেবল লগ আউট করেন তবে এটি কোনও ব্যাপার নয়।


-2

http://www.gnu.org/software/hello/manual/libc/ পরিবর্তনশীল- আরগমেন্টস- আউটপুট। html stderr এ মুদ্রণের জন্য নিম্নলিখিত উদাহরণ দেয়। পরিবর্তে আপনার লগ ফাংশনটি ব্যবহার করতে আপনি এটি পরিবর্তন করতে পারেন:

 #include <stdio.h>
 #include <stdarg.h>

 void
 eprintf (const char *template, ...)
 {
   va_list ap;
   extern char *program_invocation_short_name;

   fprintf (stderr, "%s: ", program_invocation_short_name);
   va_start (ap, template);
   vfprintf (stderr, template, ap);
   va_end (ap);
 }

ভিএফপ্রিন্টফের পরিবর্তে আপনাকে ভিএসপ্রিন্টফ ব্যবহার করতে হবে যেখানে মুদ্রণের জন্য আপনাকে পর্যাপ্ত বাফার সরবরাহ করতে হবে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.