আমি স্ক্যানফের পরিবর্তে ইনপুট রূপান্তরকরণের জন্য কী ব্যবহার করতে পারি?


125

আমি খুব ঘন ঘন লোককে অন্যকে ব্যবহার থেকে নিরুৎসাহিত হতে দেখেছি scanfএবং বলছি যে এর চেয়ে আরও ভাল বিকল্প রয়েছে। তবে, আমি যা দেখেছি তা হ'ল "ব্যবহার করবেন না scanf" বা "এখানে একটি সঠিক বিন্যাসের স্ট্রিং রয়েছে" , এবং বর্ণিত "ভাল বিকল্প" এর কোনও উদাহরণ কখনও নয় ।

উদাহরণস্বরূপ, কোডের এই স্নিপেটটি নেওয়া যাক:

scanf("%c", &c);

এটি সর্বশেষ রূপান্তরের পরে ইনপুট স্ট্রিমে থাকা শ্বেত স্থানটি পড়ে। এর স্বাভাবিক প্রস্তাবিত সমাধানটি হ'ল:

scanf(" %c", &c);

বা ব্যবহার না করা scanf

যেহেতু scanfখারাপ, scanfসাধারণত ইনপুট ফর্ম্যাটগুলি রূপান্তর করার জন্য এমন কিছু এএনএসআই সি বিকল্প কী যা সাধারণত ব্যবহার না করে হ্যান্ডেল করতে পারে (যেমন পূর্ণসংখ্যা, ভাসমান-পয়েন্ট সংখ্যা এবং স্ট্রিং) scanf?

উত্তর:


87

ইনপুট পড়ার সর্বাধিক সাধারণ উপায় হ'ল:

  • fgetsএকটি নির্দিষ্ট আকারের সাহায্যে ব্যবহার করা যা সাধারণত পরামর্শ দেওয়া হয় এবং

  • ব্যবহার করছেন fgetc, যা আপনি যদি কেবল একটি একক পড়েন তবে দরকারী হতে পারে char

ইনপুট রূপান্তর করতে, এখানে বিভিন্ন ধরণের ফাংশন রয়েছে যা আপনি ব্যবহার করতে পারেন:

  • strtollস্ট্রিংকে পূর্ণসংখ্যায় রূপান্তর করতে into

  • strtof/ d/ ld, একটি স্ট্রিংকে ভাসমান-পয়েন্ট সংখ্যাতে রূপান্তর করতে

  • sscanf, যা কেবল ব্যবহারের মতো খারাপ নয়scanf , যদিও এর নীচে উল্লিখিত বেশিরভাগ পতন রয়েছে

  • সরল এএনএসআই সি-তে সীমানা-বিচ্ছিন্ন ইনপুট পার্স করার কোনও ভাল উপায় নেই, হয় strtok_rপসিক্স থেকে ব্যবহার করুন বা strtok, যা থ্রেড-নিরাপদ নয়। এছাড়াও আপনি পারে আপনার নিজের রোল ব্যবহার থ্রেড-নিরাপদ বৈকল্পিক strcspnএবং strspnহিসাবে,strtok_r কোনও বিশেষ ওএস সমর্থন জড়িত না।

  • এটি ওভারকিল হতে পারে তবে আপনি লেক্সার এবং পার্সার ( flexএবং) ব্যবহার করতে পারেনbison সর্বাধিক সাধারণ উদাহরণ হিসাবে) ।

  • কোনও রূপান্তর নয়, কেবল স্ট্রিংটি ব্যবহার করুন


যেহেতু আমি আমার প্রশ্নে খারাপ কেন ঠিক তেমন প্রবেশ করিনি scanf, তাই আমি বিশদভাবে জানিয়ে দেব:

  • রূপান্তর নির্দিষ্টকরণের সাথে %[...]এবং %c, scanfসাদা স্থান খায় না। এই প্রশ্নের বহু নকল দ্বারা প্রমাণ হিসাবে এটি স্পষ্টতই বহুলভাবে পরিচিত নয়

  • &উল্লেখ করার সময় আনরি অপারেটরটি কখন ব্যবহার করবেন সে সম্পর্কে কিছুটা বিভ্রান্তি রয়েছেscanfএর আর্গুমেন্টগুলি (বিশেষত স্ট্রিং সহ)।

  • এর থেকে ফেরতের মূল্য উপেক্ষা করা খুব সহজ scanf । এটি সহজেই একটি অনির্দেশিত ভেরিয়েবলটি পড়া থেকে অনির্ধারিত আচরণের কারণ হতে পারে।

  • বাফার ওভারফ্লো ইন ইন রোধ করতে ভুলে যাওয়া খুব সহজ scanfscanf("%s", str)তার চেয়ে খারাপ না, যেমন খারাপgets

  • পূর্ণসংখ্যার সাথে রূপান্তর করার সময় আপনি ওভারফ্লো সনাক্ত করতে পারবেন না scanfআসলে, ওভারফ্লো এই ফাংশনগুলিতে অনির্ধারিত আচরণের কারণ হয়।



56

scanfখারাপ কেন ?

মূল সমস্যাটিscanf কখনও ব্যবহারকারীর ইনপুট নিয়ে কাজ করার উদ্দেশ্যে করা হয়নি। এটি "নিখুঁত" ফর্ম্যাট ডেটার সাথে ব্যবহার করার উদ্দেশ্যে intended আমি "নিখুঁত" শব্দটি উদ্ধৃত করেছিলাম কারণ এটি সম্পূর্ণ সত্য নয়। তবে এটি এমন ডেটা পার্স করার জন্য ডিজাইন করা হয়নি যা ব্যবহারকারীর ইনপুটের মতো অবিশ্বাস্য। প্রকৃতির দ্বারা, ব্যবহারকারী ইনপুট অনুমানযোগ্য নয়। ব্যবহারকারীরা নির্দেশাবলীর ভুল বোঝে, টাইপগুলি তৈরি করে, দুর্ঘটনাক্রমে এগুলি সম্পন্ন হওয়ার আগে এন্টার টিপুন One কেউ যুক্তিযুক্তভাবে জিজ্ঞাসা করতে পারে যে কোনও ফাংশন যা ব্যবহারকারীর ইনপুটটির জন্য ব্যবহার করা উচিত নয় তা কেন পড়ে stdin। আপনি যদি অভিজ্ঞ * নিক্স ব্যবহারকারী হন তবে ব্যাখ্যাটি চমক হিসাবে আসবে না তবে এটি উইন্ডোজ ব্যবহারকারীদের বিভ্রান্ত করতে পারে। * নিক্স সিস্টেমে, পাইপিংয়ের মাধ্যমে কাজ করা প্রোগ্রামগুলি তৈরি করা খুব সাধারণ বিষয়,stdoutstdinদ্বিতীয়। এইভাবে, আপনি আউটপুট এবং ইনপুট অনুমানযোগ্য তা নিশ্চিত করতে পারেন। এই পরিস্থিতিতে, scanfআসলে ভাল কাজ করে। অপ্রত্যাশিত ইনপুট নিয়ে কাজ করার সময় আপনি সমস্ত ধরণের সমস্যার ঝুঁকি নিয়ে থাকেন।

তাহলে ব্যবহারকারীর ইনপুটটির জন্য কেন কোনও সহজেই সহজে ব্যবহারের মানক ফাংশন নেই? এখানে কেবল অনুমান করা যায়, তবে আমি ধরে নিই যে পুরানো হার্ডকোর সি হ্যাকাররা কেবল ভেবেছিল যে তারা খুব আকিদা থাকা সত্ত্বেও বিদ্যমান ফাংশনগুলি যথেষ্ট ভাল ছিল। এছাড়াও, আপনি সাধারণত টার্মিনাল অ্যাপ্লিকেশনগুলিতে দেখেন সেগুলি খুব কমই ব্যবহারকারীর ইনপুট পড়ে stdin। প্রায়শই আপনি কমান্ড লাইন আর্গুমেন্ট হিসাবে সমস্ত ব্যবহারকারী ইনপুট পাস করেন। অবশ্যই, ব্যতিক্রম আছে, তবে বেশিরভাগ অ্যাপ্লিকেশনগুলির জন্য, ব্যবহারকারীর ইনপুটটি খুব সামান্য বিষয়।

তো তুমি কি করতে পার?

আমার প্রিয় fgetsসংমিশ্রণে sscanf। আমি এটি সম্পর্কে একবার একটি উত্তর লিখেছিলাম, তবে আমি সম্পূর্ণ কোডটি আবার পোস্ট করব। এখানে শালীন (তবে নিখুঁত নয়) ত্রুটি পরীক্ষা এবং পার্সিংয়ের একটি উদাহরণ রয়েছে। এটি ডিবাগিং উদ্দেশ্যে যথেষ্ট ভাল।

বিঃদ্রঃ

আমি বিশেষত ব্যবহারকারীকে একটি এক লাইনে দুটি ভিন্ন জিনিস ইনপুট করতে বলতে চাই না। আমি কেবল তখনই করি যখন তারা প্রাকৃতিক উপায়ে একে অপরের অন্তর্ভুক্ত। উদাহরণস্বরূপ পছন্দ করুন printf("Enter the price in the format <dollars>.<cent>: ")এবং তারপরে ব্যবহার করুন sscanf(buffer "%d.%d", &dollar, &cent)। আমি কখনও এরকম কিছু করতাম না printf("Enter height and base of the triangle: ")fgetsনীচের ব্যবহারের মূল পয়েন্টটি হ'ল ইনপুটগুলি নিশ্চিত করে নিন যাতে একটি ইনপুট পরবর্তীটিকে প্রভাবিত করে না।

#define bsize 100

void error_function(const char *buffer, int no_conversions) {
        fprintf(stderr, "An error occurred. You entered:\n%s\n", buffer);
        fprintf(stderr, "%d successful conversions", no_conversions);
        exit(EXIT_FAILURE);
}

char c, buffer[bsize];
int x,y;
float f, g;
int r;

printf("Enter two integers: ");
fflush(stdout); // Make sure that the printf is executed before reading
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) error_function(buffer, r);

// Unless the input buffer was to small we can be sure that stdin is empty
// when we come here.
printf("Enter two floats: ");
fflush(stdout);
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) error_function(buffer, r);

// Reading single characters can be especially tricky if the input buffer
// is not emptied before. But since we're using fgets, we're safe.
printf("Enter a char: ");
fflush(stdout);
if(! fgets(buffer, bsize, stdin)) error_function(buffer, 0);
if((r = sscanf(buffer, "%c", &c)) != 1) error_function(buffer, r);

printf("You entered %d %d %f %c\n", x, y, f, c);

আপনি যদি এগুলি অনেক কিছু করেন তবে আমি একটি মোড়ক তৈরি করার পরামর্শ দিতে পারি যা সর্বদা ঝাপটায়:

int printfflush (const char *format, ...)
{
   va_list arg;
   int done;
   va_start (arg, format);
   done = vfprintf (stdout, format, arg);
   fflush(stdout);
   va_end (arg);
   return done;
}```

এটি করার ফলে একটি সাধারণ সমস্যা দূর হবে, এটি হ'ল পেছনের নতুন লাইন যা নীড়ের ইনপুটটির সাথে গোলযোগ করতে পারে। তবে এটির আরও একটি সমস্যা রয়েছে, এটি যদি লাইনটির চেয়ে দীর্ঘ হয় bsize। আপনি এটি দিয়ে পরীক্ষা করতে পারেন if(buffer[strlen(buffer)-1] != '\n')। আপনি যদি নতুন লাইনটি সরাতে চান তবে আপনি এটি দিয়ে এটি করতে পারেন buffer[strcspn(buffer, "\n")] = 0

সাধারণভাবে, আমি ব্যবহারকারীকে এমন কিছু অদ্ভুত বিন্যাসে ইনপুট প্রবেশের প্রত্যাশা না করার পরামর্শ দেওয়ার পরামর্শ দেব যা আপনার বিভিন্ন ভেরিয়েবলগুলিতে পার্স করা উচিত। আপনি যদি ভেরিয়েবলগুলি বরাদ্দ করতে চান heightএবং width, একই সাথে উভয়ের জন্য জিজ্ঞাসা করবেন না। ব্যবহারকারীদের মধ্যে এন্টার টিপতে অনুমতি দিন। এছাড়াও, এই পদ্ধতিটি এক অর্থে খুব স্বাভাবিক। stdinপ্রবেশ প্রবেশ না করা পর্যন্ত আপনি কখনই ইনপুট পাবেন না , তবে কেন সর্বদা পুরো লাইনটি পড়বেন না? অবশ্যই এটি এখনও সমস্যার দিকে নিয়ে যেতে পারে যদি লাইনটি বাফারের চেয়ে দীর্ঘ হয়। আমার কি মনে আছে যে ইউজার ইনপুটটি সি-তে ক্লানকি? :)

বাফারের চেয়ে দীর্ঘতর লাইনে সমস্যা এড়াতে আপনি এমন একটি ফাংশন ব্যবহার করতে পারেন যা স্বয়ংক্রিয়ভাবে উপযুক্ত আকারের একটি বাফার বরাদ্দ করে, আপনি ব্যবহার করতে পারেন getline()। অপূর্ণতাটি হ'ল এর freeপরে আপনার ফলাফলের প্রয়োজন হবে ।

গেম ধাপে ধাপে

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


দ্রষ্টব্য যে (r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2অনুভূমিক অ-সংখ্যাযুক্ত পাঠ্যকে খারাপ হিসাবে সনাক্ত করে না।
chux - মনিকা পুনরায় ইনস্টল করুন

1
@ chux ফিক্সড% f% f। প্রথমটির সাথে আপনি কী বোঝাতে চাইছেন?
klutt

সঙ্গে fgets()এর "1 2 junk", if((r = sscanf(buffer, "%d%d", &x, &y)) != 2) {ইনপুট যদিও এটি "আবর্জনা" রয়েছে তার সাথে কিছু ভুল রিপোর্ট নেই।
chux - মনিকা

@ ছাক্স আহ, এখন দেখছি। ভাল যে ইচ্ছাকৃত ছিল।
ক্লুত

1
scanfনিখুঁত বিন্যাসিত ডেটা ব্যবহার করার উদ্দেশ্যে তৈরি করা হয়েছে তবে এটি সত্য নয়। @ জাচুস দ্বারা উল্লিখিত "জাঙ্ক" সংক্রান্ত সমস্যাটি ছাড়াও, এখানে আরও সত্য যে ফর্ম্যাটটি "%d %d %d"একটি, দুটি, বা তিনটি লাইন (বা আরও বেশি, যদি ফাঁকা লাইনগুলিতে হস্তক্ষেপ করা হয়) থেকে ইনপুট পড়তে খুশি হয়, তবে এর কোনও নেই বল (বলুন) ভালো কিছু করে দুই লাইন ইনপুট পথ "%d\n%d %d", ইত্যাদি scanfফরম্যাট জন্য উপযুক্ত হতে পারে প্রবাহ ইনপুট, কিন্তু এটি সব ভাল কিছু লাইন ভিত্তি করে নয়।
স্টিভ সামিট

18

scanfযখন আপনি জানবেন আপনার ইনপুটটি সর্বদা সুগঠিত এবং ভাল আচরণ করা হয় তখন দুর্দান্ত। তা না হলে ...

আইএমও, এখানে সর্বাধিক সমস্যা রয়েছে scanf:

  • বাফার ওভারফ্লোয়ের ঝুঁকি - আপনি যদি %sএবং %[রূপান্তর নির্দিষ্টকরণের জন্য কোনও ক্ষেত্রের প্রস্থ নির্দিষ্ট না করেন তবে আপনি বাফার ওভারফ্লো ঝুঁকিপূর্ণ (বাফার ধারণ করার চেয়ে আকারের চেয়ে বেশি ইনপুট পড়ার চেষ্টা করছেন)। দুর্ভাগ্যক্রমে, এটি আর্গুমেন্ট হিসাবে হিসাবে নির্দিষ্ট করার ভাল উপায় নেই (যেমন হিসাবে printf) - আপনাকে রূপান্তর নির্দিষ্টকরণের অংশ হিসাবে এটি হার্ডকোড করতে হবে বা কিছু ম্যাক্রো শেননিগান করতে হবে।

  • প্রত্যাখ্যান করা উচিত এমন ইনপুটগুলি গ্রহণ করে - আপনি যদি %dরূপান্তর নির্দিষ্টকারীর সাথে কোনও ইনপুট পড়েন এবং আপনি এমন 12w4কোনও কিছু টাইপ করেন তবে আপনি সেই ইনপুটটিকে scanf প্রত্যাখ্যানের প্রত্যাশা করতে পারেন , তবে তা হয় না - এটি সাফল্যের সাথে রূপান্তর করে এবং নির্ধারণ করে 12, w4ইনপুট স্ট্রিমের মধ্যে রেখে পরের পড়তে বোকা।

সুতরাং, আপনি পরিবর্তে কি ব্যবহার করা উচিত?

আমি সাধারণত সমস্ত ইন্টারেক্টিভ ইনপুটটি পাঠ্য হিসাবে ব্যবহার করে পড়ার পরামর্শ দিই fgets- এটি আপনাকে একবারে পড়ার জন্য সর্বাধিক সংখ্যক অক্ষর নির্দিষ্ট করার অনুমতি দেয়, যাতে আপনি সহজেই বাফার ওভারফ্লো প্রতিরোধ করতে পারেন:

char input[100];
if ( !fgets( input, sizeof input, stdin ) )
{
  // error reading from input stream, handle as appropriate
}
else
{
  // process input buffer
}

এর একটি বিভ্রান্তি fgetsহল যে এটি রুমে থাকলে পেছনের নিউলাইনটি বাফারে সংরক্ষণ করবে, সুতরাং আপনি যে প্রত্যাশার চেয়ে বেশি ইনপুট টাইপ করেছেন কেউ তা দেখতে সহজে পরীক্ষা করতে পারেন:

char *newline = strchr( input, '\n' );
if ( !newline )
{
  // input longer than we expected
}

আপনি কীভাবে এটির মোকাবিলা করবেন তা আপনার হাতে - আপনি হয় পুরো ইনপুটটিকে হাতছাড়া করে দিতে পারেন এবং এর সাথে বাকী কোনও ইনপুট স্লাপ করতে পারেন getchar:

while ( getchar() != '\n' ) 
  ; // empty loop

অথবা আপনি এতক্ষণ যে ইনপুটটি পেয়েছেন তা প্রক্রিয়া করতে পারেন এবং আবার পড়তে পারেন। এটি আপনি যে সমস্যার সমাধান করার চেষ্টা করছেন তার উপর নির্ভর করে।

করতে tokenize ইনপুট (এটা এক বা একাধিক বিভেদক উপর ভিত্তি করে বিভক্ত আপ), আপনি ব্যবহার করতে পারেন strtok, কিন্তু হুঁশিয়ার - strtokতার ইনপুট (এটা স্ট্রিং টারমিনেটর সঙ্গে বিভেদক ওভাররাইট হয়) পরিবর্তন, এবং আপনি তার রাষ্ট্র সংরক্ষণ করতে পারবেন না (অর্থাত, আপনি যা করতে পারেন ' টি একটি স্ট্রিংকে আংশিকভাবে টোকেনাইজ করুন, তারপরে অন্যটিকে টোকানাইজ করতে শুরু করুন, তারপরে আপনি যেখানে আসল স্ট্রিংয়ে রেখে গেছেন তা বেছে নিন)। এখানে একটি বৈকল্পিক রয়েছে, strtok_sযা টোকনাইজারের রাষ্ট্র সংরক্ষণ করে, কিন্তু আফাইক এটির বাস্তবায়ন isচ্ছিক ( __STDC_LIB_EXT1__এটি উপলব্ধ কিনা তা নির্ধারণ করার জন্য আপনাকে এটি পরীক্ষা করতে হবে)।

একবার আপনি নিজের ইনপুটটিকে টোকনাইজ করে ফেললে, আপনার যদি স্ট্রিংগুলিকে সংখ্যায় রূপান্তর করতে হয় (যেমন, "1234"=> 1234), আপনার কাছে বিকল্প রয়েছে। strtolএবং strtodপূর্ণসংখ্যা এবং আসল সংখ্যার স্ট্রিং উপস্থাপনাগুলি তাদের নিজ নিজ প্রকারে রূপান্তর করবে। 12w4আমি উপরে উল্লিখিত সমস্যাটি তারা আপনাকে ধরতেও সহায়তা করে - তাদের একটি যুক্তি প্রথম অক্ষরটির পয়েন্টার যা স্ট্রিংয়ে রূপান্তরিত হয়নি :

char *text = "12w4";
char *chk;
long val;
long tmp = strtol( text, &chk, 10 );
if ( !isspace( *chk ) && *chk != 0 )
  // input is not a valid integer string, reject the entire input
else
  val = tmp;

যদি আপনি কোনও ক্ষেত্রের প্রস্থ ... - বা কোনও রূপান্তর দমন নির্দিষ্ট না করেন (উদাহরণস্বরূপ %*[%\n], যা উত্তরের পরে লম্বা লাইনগুলি মোকাবেলার জন্য দরকারী)।
টবির স্পিড

মাঠের প্রস্থের রান-টাইম স্পেসিফিকেশন পাওয়ার একটি উপায় আছে তবে এটি ভাল নয়। আপনার কোডটিতে বিন্যাসের স্ট্রিংটি তৈরি করতে হবে (সম্ভবত ব্যবহার করছেন snprintf()),।
টবির স্পিড

5
আপনি isspace()সেখানে সর্বাধিক সাধারণ ভুল করেছেন - এটি স্বাক্ষরিত স্বাক্ষরযুক্ত অক্ষরগুলি হিসাবে স্বীকার করে int, সুতরাং unsigned charযেখানে প্লাটফর্মগুলিতে charস্বাক্ষর রয়েছে সেখানে ইউবি এড়াতে আপনাকে কাস্ট করতে হবে ।
টবির স্পিড

9

এই উত্তরে আমি ধরে নিচ্ছি যে আপনি পাঠ্যের লাইনগুলি পড়ছেন এবং ব্যাখ্যা করছেন । সম্ভবত আপনি ব্যবহারকারীকে অনুরোধ করছেন, যিনি কিছু টাইপ করছেন এবং রিটার্নটিকে হিট করছেন। অথবা সম্ভবত আপনি কোনও প্রকারের ডেটা ফাইল থেকে কাঠামোগত পাঠ্যের লাইনগুলি পড়ছেন।

আপনি যেহেতু পাঠ্যের লাইনগুলি পড়ছেন, তাই আপনার কোডটি একটি লাইব্রেরি ফাংশনটির চারপাশে সংগঠিত করা বোধগম্য হয় যা ভালভাবে পাঠ্যের একটি লাইন পড়ে। স্ট্যান্ডার্ড ফাংশনটি হ'ল fgets(), যদিও অন্য রয়েছে (সহ getline)। এবং তারপরে পরবর্তী পদক্ষেপটি হ'ল পাঠ্যের লাইনটি কোনওভাবে ব্যাখ্যা করা।

fgetsপাঠ্যের একটি লাইন পড়তে কল করার জন্য এখানে প্রাথমিক রেসিপি :

char line[512];
printf("type something:\n");
fgets(line, 512, stdin);
printf("you typed: %s", line);

এটি কেবল পাঠ্যের এক লাইনে পড়ে এবং এটি আবার ছাপে। লিখিত হিসাবে এটির বেশ কয়েকটি সীমাবদ্ধতা রয়েছে যা আমরা এক মিনিটের মধ্যেই পেয়ে যাব। এটির একটি খুব দুর্দান্ত বৈশিষ্ট্য রয়েছে: 512 নম্বরটি আমরা দ্বিতীয় যুক্তি হিসাবে পাস করেছি আমরা যে fgetsঅ্যারে পড়তে lineবলছি তার আকার fgets। এই বাস্তবতা - যে fgetsএটি পড়ার কতটুকু অনুমতি আমরা বলতে পারি - এর অর্থ হ'ল আমরা নিশ্চিত হতে পারি যে fgetsএটিতে বেশি কিছু পড়ে আরে উপচে পড়বে না।

সুতরাং এখন আমরা কীভাবে টেক্সটের একটি লাইন পড়তে জানি, তবে কী যদি আমরা সত্যিই একটি পূর্ণসংখ্যার, বা ভাসমান-পয়েন্ট নম্বর, বা একটি একক অক্ষর, বা একটি শব্দটি পড়তে চাইতাম? (অর্থাৎ, কি যদি scanfকল আমরা উন্নত করতে চেষ্টা করছেন একটি বিন্যাসে সুনির্দিষ্টভাবে উল্লেখ করা ব্যবহার ছিল %d, %f, %cবা, %s?)

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

printf("type an integer:\n");
fgets(line, 512, stdin);
int i = atoi(line);
printf("type a floating-point number:\n");
fgets(line, 512, stdin);
float f = atof(line);
printf("you typed %d and %f\n", i, f);

আপনি যদি ব্যবহারকারীকে একটি অক্ষর টাইপ করতে চান (সম্ভবত yবা nহ্যাঁ / কোনও প্রতিক্রিয়া হিসাবে), আপনি আক্ষরিকভাবে কেবল লাইনের প্রথম অক্ষরটি ধরে নিতে পারেন:

printf("type a character:\n");
fgets(line, 512, stdin);
char c = line[0];
printf("you typed %c\n", c);

(এটি অবশ্যই ব্যবহারকারী একাধিক-চরিত্রের প্রতিক্রিয়া টাইপ করে এমন সম্ভাবনা উপেক্ষা করে; এটি টাইপ করা কোনও অতিরিক্ত অক্ষর চুপচাপ উপেক্ষা করে))

অবশেষে, আপনি যদি ইনপুট লাইনের চিকিত্সা করতে চান তবে ব্যবহারকারীর স্পষ্টভাবে স্পেস না থাকা একটি স্ট্রিং টাইপ করতে চাইলে

hello world!

স্ট্রিংটি "hello"অন্য কোনও কিছু অনুসরণ করেছিল (যা scanfফর্ম্যাটটি এটিই %sকরত), ভাল, সেই ক্ষেত্রে আমি কিছুটা ফাইব্বড করেছিলাম, লাইনটিকে সেভাবে পুনরায় ব্যাখ্যা করা এত সহজ নয়, সুতরাং, তার উত্তরটি প্রশ্নের অংশটি কিছুটা অপেক্ষা করতে হবে।

তবে প্রথমে আমি তিনটি জিনিস ফিরে যেতে চাই I

(1) আমরা কল করছি

fgets(line, 512, stdin);

অ্যারেতে পড়তে line, এবং যেখানে 512 অ্যারের আকার lineতাই fgetsএটি প্রবাহিত করতে না জানে। তবে এটি নিশ্চিত করতে যে 512 সঠিক নম্বর (বিশেষত, কেউ আকার পরিবর্তন করার জন্য প্রোগ্রামটি টুইট করেছে কিনা তা খতিয়ে দেখার জন্য), আপনাকে যেখানে lineঘোষণা করা হয়েছিল সেখানে ফিরে যেতে হবে । এটি একটি উপদ্রব, সুতরাং আকারগুলি সিঙ্কে রাখার জন্য আরও দুটি আরও ভাল উপায়। আপনি (ক) আকারের জন্য একটি নাম তৈরি করতে প্রিপ্রসেসর ব্যবহার করতে পারেন:

#define MAXLINE 512
char line[MAXLINE];
fgets(line, MAXLINE, stdin);

অথবা, (খ) সি এর sizeofঅপারেটর ব্যবহার করুন :

fgets(line, sizeof(line), stdin);

(২) দ্বিতীয় সমস্যাটি হ'ল আমরা ত্রুটির জন্য যাচাই করিনি। আপনি যখন ইনপুটটি পড়ছেন, আপনার সর্বদা ত্রুটির সম্ভাবনা যাচাই করা উচিত । যদি কোনও কারণে fgetsআপনি পাঠ্যর রেখাটি পড়তে না চাইতে পারেন তবে এটি নাল পয়েন্টারটি ফিরিয়ে দিয়ে বোঝায়। সুতরাং আমাদের মত জিনিস করা উচিত ছিল

printf("type something:\n");
if(fgets(line, 512, stdin) == NULL) {
    printf("Well, never mind, then.\n");
    exit(1);
}

শেষ fgetsঅবধি , এখানে একটি সমস্যা আছে যে পাঠ্যগুলির একটি লাইন পড়ার জন্য, অক্ষরগুলি পড়ে এবং আপনার অ্যারে পূরণ করে যতক্ষণ না এটি \nলাইনটি বন্ধ করে এমন অক্ষর খুঁজে না পায় এবং এটি \nঅক্ষরটি আপনার অ্যারেতেও পূরণ করে । আপনি যদি আমাদের আগের উদাহরণটি সামান্য পরিবর্তন করেন তবে আপনি এটি দেখতে পারেন:

printf("you typed: \"%s\"\n", line);

আমি যদি এটি চালিয়ে থাকি এবং যখন আমাকে জিজ্ঞাসা করে তখন "স্টিভ" টাইপ করুন, এটি প্রিন্ট করে

you typed: "Steve
"

যে "দ্বিতীয় লাইন উপর কারণ স্ট্রিং এটা পড়তে এবং ফিরে মুদ্রিত আউট আসলে ছিল "Steve\n"

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

এই মুহুর্তে, আপনি চিন্তা হতে পারে: "আমার মনে হল তুমি বলেছিলে scanf কোন ভাল ছিল, এবং এই অন্যান্য উপায় এত ভাল হবে কিন্তু। fgetsএকটি উত্পাত মত চেহারা শুরু হয় কলিং। scanfছিল এত সহজ আমি এটা ব্যবহার করা চালিয়ে যাবে না? "

অবশ্যই, আপনি চাইলে ব্যবহার চালিয়ে scanfযেতে পারেন। (এবং সত্যিকারের সরল জিনিসের জন্য, কিছু উপায়ে এটি সহজ) প্রত্যাশা করেনি, বা যখন আপনি আরও জটিল কিছু করতে এটি কীভাবে ব্যবহার করতে পারেন তা বুঝতে পারেন না। এবং আসুন fgetsআসল উপদ্রবগুলি একবার দেখে নিই :

  1. আপনাকে সর্বদা অ্যারের আকার নির্দিষ্ট করতে হবে। ঠিক আছে, অবশ্যই এটি কোনও উপদ্রব নয় - এটি একটি বৈশিষ্ট্য, কারণ বাফার ওভারফ্লো একটি সত্যই খারাপ জিনিস।

  2. আপনাকে ফেরতের মানটি পরীক্ষা করতে হবে। প্রকৃতপক্ষে, এটি একটি ধোয়া, কারণ scanfসঠিকভাবে ব্যবহার করতে গেলে আপনাকে এর রিটার্নের মানটিও পরীক্ষা করতে হবে।

  3. আপনাকে \nপিছনে খুলে ফেলতে হবে। এটি, আমি স্বীকার করি, একটি সত্য উপদ্রব। আমি চাই যে কোনও স্ট্যান্ডার্ড ফাংশন ছিল আমি আপনাকে এটি চিহ্নিত করতে পারতাম যে এই সামান্য সমস্যা ছিল না। (দয়া করে কেউই সামনে আনবেন না gets)) তবে scanf's১ different টি ভিন্ন উপদ্রবের তুলনায় আমি যে fgetsকোনও দিনের এই উপদ্রবটি গ্রহণ করব ।

তাহলে আপনি কীভাবে নতুন লাইনের স্ট্রিপ করবেন ? তিনটি উপায়:

(ক) সুস্পষ্ট উপায়:

char *p = strchr(line, '\n');
if(p != NULL) *p = '\0';

(খ) কৌতুকপূর্ণ এবং কমপ্যাক্ট উপায়:

strtok(line, "\n");

দুর্ভাগ্যক্রমে এটি সর্বদা কাজ করে না।

(গ) আরেকটি কমপ্যাক্ট এবং হালকা অস্পষ্ট উপায়:

line[strcspn(line, "\n")] = '\0';

এর অসম্পূর্ণতা: এবং এখন যে যে ভাবে বাইরে, আমরা ফিরে অন্য জিনিস আমি উপর এড়ানো পেতে পারেন atoi()এবং atof()। এগুলির সাথে সমস্যা হ'ল এগুলি আপনাকে সাফল্য বা ব্যর্থতার সাফল্যের কোনও কার্যকর ইঙ্গিত দেয় না: তারা চুপচাপ অনুন্নত সংখ্যাসূচক ইনপুটটি উপেক্ষা করে এবং তারা যদি চূড়ান্তভাবে 0 সংখ্যার ইনপুট না থাকে তবে 0 করে ফিরে আসে। পছন্দের বিকল্পগুলি - যার কয়েকটি অন্যান্য সুবিধাও রয়েছে - strtolএবং তা strtodstrtolএছাড়াও আপনাকে 10 ব্যতীত একটি বেস ব্যবহার করতে দেয় যার অর্থ আপনি (অন্যান্য জিনিসের মধ্যে) এর সাথে %oবা এর %xসাথে পেতে পারেনscanf। তবে কীভাবে এই ফাংশনগুলি সঠিকভাবে ব্যবহার করতে হবে তা দেখানো একটি গল্প এবং এটি ইতিমধ্যে একটি সুন্দর খণ্ডিত গল্পে রূপান্তরিত হওয়া থেকে খুব বেশি বিচ্যুতি হবে, তাই আমি এখন সেগুলি সম্পর্কে আরও কিছু বলব না।

বাকি মূল বিবরণ সংক্রান্ত উদ্বেগ ইনপুটটি আপনি সম্ভবত একটি একক সংখ্যা বা চরিত্রের চেয়ে জটিল যেটিকে বিশ্লেষণের চেষ্টা করছেন। আপনি যদি দুটি সংখ্যা, বা একাধিক শ্বেত স্পেস-পৃথক শব্দ, বা নির্দিষ্ট ফ্রেমিং বিরামচিহ্ন যুক্ত একটি লাইন পড়তে চান? এটিই জিনিসগুলি আকর্ষণীয় হয়ে উঠেছে scanf, এবং আপনি যদি জিনিসগুলি ব্যবহার করার চেষ্টা করছেন তবে জিনিসগুলি সম্ভবত জটিল হয়ে উঠছিল এবং যেখানে এখন আরও অনেক বিকল্প রয়েছে যে আপনি পরিষ্কারভাবে একটি পাঠ্য ব্যবহার করে পাঠ্য পাঠ করেছেন fgets, যদিও এই সমস্ত বিকল্পগুলির সম্পূর্ণ গল্প সম্ভবত কোনও বই পূরণ করতে পারে, তাই আমরা কেবল এখানে পৃষ্ঠটি স্ক্র্যাচ করতে সক্ষম হব।

  1. আমার প্রিয় কৌশলটি হ'ল স্পেস-স্পেস-বিচ্ছিন্ন "শব্দের" বিভক্ত করা, তারপরে প্রতিটি "শব্দ" দিয়ে আরও কিছু করা। এটি করার জন্য একটি প্রধান স্ট্যান্ডার্ড ফাংশন strtok(এটিতে এর সমস্যাগুলিও রয়েছে এবং এটি সম্পূর্ণ পৃথক আলোচনাকেও রেট দেয়)। আমার নিজস্ব পছন্দগুলি প্রতিটি ভাঙা-বিচ্ছিন্ন "শব্দ" -এর জন্য পয়েন্টারগুলির একটি অ্যারে তৈরির জন্য একটি উত্সর্গীকৃত ফাংশন, আমি এই কোর্স নোটগুলিতে বর্ণনা করি এমন একটি ফাংশন । যে কোনও হারে, একবার আপনি "শব্দ" পেয়ে গেলে আপনি প্রতিটিটিকে আরও প্রক্রিয়া করতে পারেন, সম্ভবত আমরা ইতিমধ্যে দেখেছি একই atoi/ atof/ strtol/ strtodফাংশনগুলির সাথে।

  2. স্ববিরোধী মনে হলেও এটা, যদিও আমরা figuring আউট কিভাবে থেকে দূরে সরাতে সময় ও শ্রম এখান থেকে ন্যায্য পরিমাণ খরচ করে থাকেন scanfআমরা শুধু সঙ্গে পড়া লেখার লাইন দিয়ে মোকাবেলা করতে আরেকটি চমৎকার উপায় fgetsএটা পাস হয় sscanf। এইভাবে, আপনি বেশিরভাগ সুবিধার সাথে শেষ করেন scanfতবে বেশিরভাগ অসুবিধা ছাড়াই।

  3. যদি আপনার ইনপুট সিনট্যাক্সটি বিশেষ জটিল হয় তবে এটি পার্স করার জন্য একটি "রেজিএক্সএক্স" লাইব্রেরি ব্যবহার করা উপযুক্ত হবে।

  4. অবশেষে, আপনি যে উপযুক্ত অ্যাডহক পার্সিং সলিউশনগুলির জন্য উপযুক্ত তা ব্যবহার করতে পারেন । আপনি char *যে অক্ষরের প্রত্যাশা করেছেন তার জন্য পয়েন্টার চেক করে আপনি একবারে একটি অক্ষরেখার মধ্য দিয়ে যেতে পারেন। অথবা আপনি ফাংশন ব্যবহার করে নির্দিষ্ট অক্ষরের জন্য অনুসন্ধান করতে পারেন পছন্দ strchrবা strrchr, বা strspnবা strcspnবা strpbrk। অথবা আমরা পূর্বে যে এড়িয়ে গেছি strtolবা strtodফাংশনগুলি ব্যবহার করে ডিজিটাল অক্ষরের গোষ্ঠীগুলি বিশ্লেষণ / রূপান্তর করতে এবং এড়িয়ে যেতে পারি।

স্পষ্টতই আরও অনেক কিছু বলা যেতে পারে, তবে আশা করি এই ভূমিকা আপনাকে শুরু করবে।


সহজ লেখার sizeof (line)চেয়ে লেখার উপযুক্ত কারণ আছে কি sizeof line? প্রাক্তনটি এটিকে দেখতে lineএকটি টাইপের নাম বলে মনে হচ্ছে!
টবির স্পিড

@ টবিস্পাইট একটি ভাল কারণ? না, আমি সন্দেহ করি। প্রথম বন্ধনীগুলি আমার অভ্যাস, কারণ এটির জন্য প্রয়োজনীয় জিনিসগুলির নাম বা টাইপ নাম রয়েছে কিনা তা আমার মনে করার উদ্রেক করা যায় না, তবে অনেক প্রোগ্রামার যখন পারেন তখন এগুলি ছেড়ে যান। (আমার কাছে এটি ব্যক্তিগত পছন্দ এবং শৈলীর বিষয়, এবং এটিতে খুব সুন্দর একটি ছোট বিষয়
স্টিভ সামিট

sscanfরূপান্তর ইঞ্জিন হিসাবে ব্যবহারের জন্য কিন্তু একটি আলাদা সরঞ্জাম দিয়ে ইনপুট সংগ্রহ (এবং সম্ভবত ম্যাসেজ করা) করতে হবে। তবে হয়তো getlineতাহাত প্রসঙ্গে উল্লেখযোগ্য ।
dmckee --- প্রাক্তন-মডারেটর বিড়ালছানা

আপনি যখন " fscanf" সত্যিকারের উপদ্রবগুলি " সম্পর্কে কথা বলেন, আপনার অর্থ কি fgets? এবং উপদ্রব # 3 সত্যিই আমাকে বিরক্ত করে, বিশেষত প্রদত্ত scanfঅক্ষরের ইনপুট (যা নিউলাইনটিকে অনেক ক্লিনার ছাড়িয়ে দেবে) এর পরিবর্তে বাফারে একটি অকেজো পয়েন্টার দেয়।
সুপারক্যাট

1
আপনার sizeofশৈলীর ব্যাখ্যার জন্য ধন্যবাদ । আমার জন্য, আপনি যখন প্যারেনগুলি দেখেন তখন মনে রাখা সহজ: আমি (type)মূল্য ছাড়াই অভিনেতাদের মতো হওয়ার কথা ভাবি (কারণ আমরা কেবল টাইপটিতে আগ্রহী)। অন্য একটি জিনিস: আপনি বলছেন যে strtok(line, "\n")সবসময় কার্যকর হয় না, তবে কখনই তা সম্ভব না তা স্পষ্ট নয়। আমি অনুমান করছি আপনি সেই মামলার কথা ভাবছেন যেখানে লাইফটি বাফারের চেয়ে দীর্ঘ ছিল, সুতরাং আমাদের কোনও নতুন লাইন নেই, এবং strtok()ফিরে আসে না? এটি সত্যিকারের করুণা fgets()আরও কার্যকর মূল্য ফিরিয়ে দেয় না যাতে আমরা জানতে পারি যে নিউলাইনটি আছে কিনা।
টবি স্পিড

7

স্ক্যানফের পরিবর্তে ইনপুট পার্স করতে আমি কী ব্যবহার করতে পারি?

পরিবর্তে scanf(some_format, ...), fgets()সঙ্গে বিবেচনা করুনsscanf(buffer, some_format_and %n, ...)

ব্যবহারের মাধ্যমে " %n"কোডটি সহজেই সনাক্ত করতে পারে যে সমস্ত ফর্ম্যাটটি সফলভাবে স্ক্যান করা হয়েছে এবং কোনও অতিরিক্ত অ-সাদা-স্পেস জাঙ্ক শেষের দিকে ছিল কিনা।

// scanf("%d %f fred", &some_int, &some_float);
#define EXPECTED_LINE_MAX 100
char buffer[EXPECTED_LINE_MAX * 2];  // Suggest 2x, no real need to be stingy.

if (fgets(buffer, sizeof buffer, stdin)) {
  int n = 0;
  // add ------------->    " %n" 
  sscanf(buffer, "%d %f fred %n", &some_int, &some_float, &n);
  // Did scan complete, and to the end?
  if (n > 0 && buffer[n] == '\0') {
    // success, use `some_int, some_float`
  } else {
    ; // Report bad input and handle desired.
  }

6

আসুন পার্সিংয়ের প্রয়োজনীয়তাগুলি এ হিসাবে বর্ণনা করুন:

  • বৈধ ইনপুট গ্রহণ করতে হবে (এবং অন্য কোনও রূপে রূপান্তরিত)

  • অবৈধ ইনপুট অবশ্যই প্রত্যাখ্যান করা উচিত

  • যখন কোনও ইনপুট প্রত্যাখ্যান করা হয়, তখন ব্যবহারকারীকে একটি বর্ণনামূলক বার্তা সরবরাহ করা প্রয়োজন যা ব্যাখ্যা করে (স্পষ্টভাবে "সাধারণ মানুষ যারা প্রোগ্রামার নয় এমন ভাষা দ্বারা বোঝা যায়") কেন তা প্রত্যাখ্যান করা হয়েছিল (যাতে লোকেরা কীভাবে ঠিক করতে হয় তা নির্ধারণ করতে পারে) সমস্যা)

জিনিসগুলি খুব সাধারণ রাখতে, আসুন একটি একক সাধারণ দশমিক পূর্ণসংখ্যা (যা ব্যবহারকারী টাইপ করেছিলেন) এবং অন্য কোনও কিছুই পার্সিং বিবেচনা করে। ব্যবহারকারীর ইনপুট প্রত্যাখ্যান করার সম্ভাব্য কারণগুলি হ'ল:

  • ইনপুটটিতে অগ্রহণযোগ্য অক্ষর রয়েছে
  • ইনপুট এমন একটি সংখ্যা উপস্থাপন করে যা স্বীকৃত সর্বনিম্নের চেয়ে কম
  • ইনপুট এমন একটি সংখ্যা উপস্থাপন করে যা স্বীকৃত সর্বোচ্চের চেয়ে বেশি higher
  • ইনপুট এমন একটি সংখ্যার প্রতিনিধিত্ব করে যার শূন্য-বিভাজক অংশ রয়েছে

আসুন "ইনপুটটিকে অগ্রহণযোগ্য অক্ষরযুক্ত" সঠিকভাবে সংজ্ঞায়িত করা যাক; এবং বলুন যে:

  • শীর্ষস্থানীয় হোয়াইটস্পেস এবং পেছনের সাদা স্থান উপেক্ষা করা হবে (উদাঃ "
    5" 5" হিসাবে বিবেচিত হবে)
  • শূন্য বা এক দশমিক বিন্দু অনুমোদিত (যেমন "1234." এবং "1234.000" উভয়ই "1234" হিসাবে একই রকম আচরণ করা হবে)
  • কমপক্ষে একটি সংখ্যা অবশ্যই থাকবে (যেমন "।" প্রত্যাখ্যান করা হয়েছে)
  • এক দশমিক বিন্দুর বেশি অনুমোদিত নয় (যেমন "1.2.3" প্রত্যাখ্যান করা হয়েছে)
  • অঙ্কগুলির মধ্যে নয় এমন কমা প্রত্যাখ্যান করা হবে (যেমন ", 1234" প্রত্যাখ্যান করা হবে)
  • দশমিক বিন্দুর পরে থাকা কমা প্রত্যাখ্যান করা হবে (যেমন "1234.000,000" প্রত্যাখ্যান করা হবে)
  • অন্য কমা হওয়ার পরে থাকা কমাগুলি প্রত্যাখ্যানিত হয় (যেমন "1, 234" প্রত্যাখ্যান করা হয়)
  • অন্যান্য সমস্ত কমা উপেক্ষা করা হবে (যেমন "1,234" "1234" হিসাবে বিবেচিত হবে)
  • একটি বিয়োগ চিহ্ন যা প্রথম অ-শ্বেতস্থান অক্ষর নয় তা প্রত্যাখ্যান করা হয়
  • একটি ইতিবাচক চিহ্ন যা প্রথম অ-শ্বেতস্থান অক্ষর নয় তা প্রত্যাখ্যান করা হয়

এটি থেকে আমরা নির্ধারণ করতে পারি যে নিম্নলিখিত ত্রুটি বার্তাগুলি প্রয়োজন:

  • "ইনপুট শুরুতে অজানা অক্ষর"
  • "ইনপুট শেষে অজানা অক্ষর"
  • "ইনপুটটির মাঝখানে অজানা অক্ষর"
  • "সংখ্যাটি খুব কম (সর্বনিম্ন হল ....)"
  • "সংখ্যাটি খুব বেশি (সর্বোচ্চটি ....)"
  • "সংখ্যাটি পূর্ণসংখ্যা নয়"
  • "অনেক বেশি দশমিক পয়েন্ট"
  • "দশমিক সংখ্যা নেই"
  • "সংখ্যার শুরুতে খারাপ কমা"
  • "সংখ্যার শেষে খারাপ কমা"
  • "সংখ্যার মাঝখানে খারাপ কমা"
  • "দশমিক বিন্দুর পরে খারাপ কমা"

এই বিন্দু থেকে আমরা দেখতে পাচ্ছি যে একটি স্ট্রিংকে একটি পূর্ণসংখ্যার রূপান্তর করতে একটি উপযুক্ত ফাংশনটির খুব বিভিন্ন ধরণের ত্রুটির মধ্যে পার্থক্য করা প্রয়োজন; এবং এটি " scanf()" বা " atoi()" বা " strtoll()" এর মতো কিছু সম্পূর্ণ এবং সম্পূর্ণরূপে নিরর্থক কারণ তারা আপনাকে ইনপুটটির সাথে কী ভুল ছিল তার কোনও ইঙ্গিত দিতে ব্যর্থ হয় (এবং কীটি বৈধ নয় তার সম্পূর্ণ অপ্রাসঙ্গিক এবং অনুপযুক্ত সংজ্ঞা ব্যবহার করে) ইনপুট").

পরিবর্তে, এমন কিছু লিখতে শুরু করি যা অকেজো নয়:

char *convertStringToInteger(int *outValue, char *string, int minValue, int maxValue) {
    return "Code not implemented yet!";
}

int main(int argc, char *argv[]) {
    char *errorString;
    int value;

    if(argc < 2) {
        printf("ERROR: No command line argument.\n");
        return EXIT_FAILURE;
    }
    errorString = convertStringToInteger(&value, argv[1], -10, 2000);
    if(errorString != NULL) {
        printf("ERROR: %s\n", errorString);
        return EXIT_FAILURE;
    }
    printf("SUCCESS: Your number is %d\n", value);
    return EXIT_SUCCESS;
}

বর্ণিত প্রয়োজনীয়তা মেটাতে; এই convertStringToInteger()ফাংশনটি নিজে থেকেই কোডের কয়েকশ লাইন হয়ে যাওয়ার সম্ভাবনা রয়েছে।

এখন, এটি কেবল "একক সাধারণ দশমিক পূর্ণসংখ্যাকে পার্স করা"। কল্পনা করুন আপনি যদি কোনও জটিল কিছু বিশ্লেষণ করতে চান; "নাম, রাস্তার ঠিকানা, ফোন নম্বর, ইমেল ঠিকানা" কাঠামোর তালিকার মতো; বা সম্ভবত একটি প্রোগ্রামিং ভাষার মত। এই ক্ষেত্রেগুলির জন্য আপনাকে একটি পার্স তৈরি করতে কয়েক হাজার লাইন কোড লিখতে হবে যা কোনও পঙ্গু রসিকতা নয়।

অন্য কথায় ...

স্ক্যানফের পরিবর্তে ইনপুট পার্স করতে আমি কী ব্যবহার করতে পারি?

আপনার প্রয়োজনীয়তার সাথে মানিয়ে নিতে নিজের কোড কোডের (সম্ভাব্য কয়েক হাজার লাইন) লিখুন।


5

flexএকটি সাধারণ ইনপুট স্ক্যান করতে ব্যবহার করার উদাহরণ এখানে দেওয়া আছে, এক্ষেত্রে ASCII ভাসমান পয়েন্ট সংখ্যাগুলির একটি ফাইল যা ইউএস ( n,nnn.dd) বা ইউরোপীয় ( n.nnn,dd) ফর্ম্যাটে হতে পারে । এটি কেবলমাত্র একটি বৃহত্তর প্রোগ্রাম থেকে অনুলিপি করা হয়েছে, তাই কিছু অমীমাংসিত উল্লেখ থাকতে পারে:

/* This scanner reads a file of numbers, expecting one number per line.  It  */
/* allows for the use of European-style comma as decimal point.              */

%{
  #include <stdlib.h>
  #include <stdio.h>
  #include <string.h>
  #ifdef WINDOWS
    #include <io.h>
  #endif
  #include "Point.h"

  #define YY_NO_UNPUT
  #define YY_DECL int f_lex (double *val)

  double atofEuro (char *);
%}

%option prefix="f_"
%option nounput
%option noinput

EURONUM [-+]?[0-9]*[,]?[0-9]+([eE][+-]?[0-9]+)?
NUMBER  [-+]?[0-9]*[\.]?[0-9]+([eE][+-]?[0-9]+)?
WS      [ \t\x0d]

%%

[!@#%&*/].*\n

^{WS}*{EURONUM}{WS}*  { *val = atofEuro (yytext); return (1); }
^{WS}*{NUMBER}{WS}*   { *val = atof (yytext); return (1); }

[\n]
.


%%

/*------------------------------------------------------------------------*/

int scan_f (FILE *in, double *vals, int max)
{
  double *val;
  int npts, rc;

  f_in = in;
  val  = vals;
  npts = 0;
  while (npts < max)
  {
    rc = f_lex (val);

    if (rc == 0)
      break;
    npts++;
    val++;
  }

  return (npts);
}

/*------------------------------------------------------------------------*/

int f_wrap ()
{
  return (1);
}

-5

অন্যান্য উত্তরগুলি সঠিক নিম্ন-স্তরের বিশদ দেয়, সুতরাং আমি নিজেকে একটি উচ্চ-স্তরের মধ্যে সীমাবদ্ধ রাখি: প্রথমে, প্রতিটি ইনপুট লাইনটি দেখতে আপনি কী আশা করেন তা বিশ্লেষণ করুন। আনুষ্ঠানিক বাক্য গঠন সহ ইনপুটটি বর্ণনা করার চেষ্টা করুন - ভাগ্য সহ, আপনি এটি দেখতে পাবেন যে এটি নিয়মিত ব্যাকরণ , বা কমপক্ষে একটি প্রসঙ্গ-মুক্ত ব্যাকরণ ব্যবহার করে বর্ণনা করা যেতে পারে । যদি একটি নিয়মিত ব্যাকরণ যথেষ্ট হয়, তবে আপনি সসীম-রাষ্ট্রের মেশিনটি কোড আপ করতে পারেনযা একবারে প্রতিটি কমান্ড-লাইন একটি অক্ষরকে স্বীকৃতি দেয় এবং ব্যাখ্যা করে। তারপরে আপনার কোডটি একটি লাইন পড়বে (অন্যান্য জবাবগুলিতে ব্যাখ্যা করা হয়েছে), তারপরে রাষ্ট্র-মেশিনের মাধ্যমে বাফারে অক্ষরগুলি স্ক্যান করবে। নির্দিষ্ট কিছু স্থানে আপনি থামিয়ে স্ট্রিংগুলিকে স্ক্যান করা এইভাবে কোনও সংখ্যায় বা যাই হোক না কেন রূপান্তর করতে পারেন। আপনি যদি সম্ভবত এটি 'নিজের নিজের রোল' করতে পারেন তবে তা যদি সহজ হয়; যদি আপনি দেখতে পান যে আপনার সম্পূর্ণ প্রাসঙ্গিক মুক্ত ব্যাকরণের প্রয়োজন রয়েছে তবে বিদ্যমান পার্সিং সরঞ্জামগুলি কীভাবে ব্যবহার করবেন (পুনরায় lexএবং yaccতাদের রূপগুলি) কীভাবে তা নির্ধারণ করা ভাল ।


একটি সসীম রাষ্ট্র মেশিন ওভারকিল হতে পারে; রূপান্তরগুলিতে ওভারফ্লো সনাক্ত করার সহজ উপায় (যেমন errno == EOVERFLOWব্যবহারের পরে পরীক্ষা করা strtollসম্ভব) সম্ভব।
এসএস আন

1
আপনি যখন আপনার নিজস্ব সীমাবদ্ধ রাষ্ট্রের মেশিনকে কোড করবেন, যখন ফ্লেক্সগুলি এগুলিকে তুচ্ছভাবে সহজ করে তোলে?
জামেস্কেফ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.