كيف يمكنني تحليل الحقول في سلسلة مفصولة بفواصل باستخدام SSCANF أثناء دعم الحقول الفارغة؟

StackOverflow https://stackoverflow.com/questions/1508754

سؤال

لدي سلسلة مفصولة بفواصل قد تحتوي على حقول فارغة. علي سبيل المثال:

1,2,,4

باستخدام الأساسية

sscanf(string,"%[^,],%[^,],%[^,],%[^,],%[^,]", &val1, &val2, &val3, &val4);

أحصل على جميع القيم قبل الحقل الفارغ، والنتائج غير المتوقعة من الحقل الفارغ فصاعدا.

عندما أقوم بإزالة التعبير عن الحقل الفارغ من SSCANF ()،

sscanf(string,"%[^,],%[^,],,%[^,],%[^,]", &val1, &val2, &val3, &val4);

كل شيء يعمل بشكل جيد.

نظرا لأنني لا أعرف متى سأحصل على حقل فارغ، هل هناك طريقة لإعادة كتابة التعبير للتعامل مع الحقول الفارغة بأناقة؟

هل كانت مفيدة؟

المحلول

كما ترى strtok مع Comma كحرف فاصل، ستحصل على قائمة بالسلاسل الأولى أو أكثر منها ستكون بطول فارغة / صفر.

إلقاء نظرة على بلدي الجواب هنا للمزيد من المعلومات.

نصائح أخرى

رجل sscanf.:

[ يطابق أ غير فارغ تسلسل الأحرف من مجموعة الأحرف المقبولة المحددة؛

(تم اضافة التأكيدات).

يبدو أن هذا يتعامل حاليا مع قيم CSV. إذا كنت بحاجة إلى تمديدها للتعامل مع السلاسل المعروضة (بحيث يمكن أن تحتوي الحقول على فواصل، على سبيل المثال)، ستجد أن scanf-إزالة لا يمكن التعامل مع جميع تعقيدات التنسيق. وبالتالي، ستحتاج إلى استخدام التعليمات البرمجية مصممة خصيصا للتعامل مع تنسيق CSV (المتغير الخاص بك).

ستجد مناقشة تطبيقات مكتبة CSV المحددة في "ممارسة البرمجة"- في C و C ++. لا شك أن هناك العديد من الآخرين المتاحة.

إليك نسختي لمسح قيم Int Comma مفصولة. الكود الكشف عن الحقول الفارغة وغير الصحيحة.

#include <stdio.h> 
#include <string.h> 

int main(){
  char str[] = " 1 , 2 x, , 4 ";
  printf("str: '%s'\n", str );

  for( char *s2 = str; s2; ){
    while( *s2 == ' ' || *s2 == '\t' ) s2++;
    char *s1 = strsep( &s2, "," );
    if( !*s1 ){
      printf("val: (empty)\n" );
    }
    else{
      int val;
      char ch;
      int ret = sscanf( s1, " %i %c", &val, &ch );
      if( ret != 1 ){
        printf("val: (syntax error)\n" );
      }
      else{
        printf("val: %i\n", val );
      }
    }
  }

  return 0;
}

نتيجة:

str: ' 1 , 2 x, , 4 '
val: 1
val: (syntax error)
val: (empty)
val: 4

ضع "*" بعد "٪" لتخطي القراءة. بالإضافة إلى ذلك، من الممكن قراءة 3 أحرف فقط إشارة إلى "٪ 3S" على سبيل المثال.

وصلت إلى هنا تبحث عن إجابات لنفس السؤال. لم أكن أرغب في المغادرة وراء Funcion Scanf أيضا. في النهاية، أقوم ببناء ZSSCANF بنفسي، حيث تحلل التنسيق، SSCanf'ed كل بيانات واحدة تلو الأخرى والتحقق من عودة SSCANF لمعرفة ما إذا كان لدي قراءة فارغة في أي. كانت هذه حالتي الخاصة إلى حد ما: أردت فقط بعض الحقول، وبعضها يمكن أن يكون فارغا، ولا يمكن أن يفترض الفاصل.

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

int zsscanf(char *data, char *format, ...)
{
    va_list argp;
    va_start(argp, format);
    int fptr = 0, sptr = 0, iptr = 0, isptr = 0, ok, saved = 0;
    char def[32];
    while (1)
    {
        if (format[fptr] != '%')
        {
            ok = sscanf(&format[fptr], "%28[^%]%n", def, &iptr);
            if (!ok) break;
            fptr += iptr;
            def[iptr] = '%';
            def[iptr+1] = 'n';
            def[iptr+2] = 0;
            ok = sscanf(&data[sptr], def, &isptr);
            if (!ok) break;
            sptr += isptr;
        }
        else
            if (format[fptr+1] == '%')
            {
                if (data[sptr] == '%')
                {
                    fptr += 2;
                    sptr += 1;
                }
                else
                {
                    ok = -1;
                    break;
                }
            }
            else
            {
                void *savehere = NULL;
                ok = sscanf(&format[fptr], "%%%28[^%]%n", &def[1], &iptr);
                if (!ok) break;
                fptr += iptr;
                def[0] = '%';
                def[iptr] = '%';
                def[iptr+1] = 'n';
                def[iptr+2] = 0;
                isptr = 0;
                if (def[1] != '*')
                {
                    savehere = va_arg(argp, void*);
                    ok = sscanf(&data[sptr], def, savehere, &isptr);
                    if (ok == 0 && isptr == 0)
                    {
                        // Let's assume only char types. Won't hurt in other cases.
                        ((char*)savehere)[0] = 0;
                        ok = 1;
                    }
                    if (ok > 0)
                    {
                        saved++;
                    }
                }
                else
                {
                    ok = sscanf(&data[sptr], def, &isptr) == 0;
                }
                if (ok < 0) break;
                sptr += isptr;
            }
    }
    va_end(argp);
    return saved == 0 ? ok : saved;
}

int main()
{
    char *format = "%15[^\t;,]%*1[\t;,]" // NameId
                   "%*[^\t;,]%*1[\t;,]" // Name
                   "%*[^\t;,]%*1[\t;,]" // Abbreviation
                   "%*[^\t;,]%*1[\t;,]" // Description
                   "%31[^\t;,]"; // Electrical Line
    char nameId[16];
    char elect[32];
    char *line1 = "TVC-CCTV-0002\tTVC-CCTV-0002\tTVC-CCTV-0002\tCCTV DOMO CAMERA 21-32-29\tELECTRICAL_TopoLine_823\tfoo\tbar";
    char *line2 = "TVC-CCTV-0000;;;;;foo;bar;";

    int ok = zsscanf(line1, format, nameId, elect);
    printf ("%d: |%s|%s|\n", ok, nameId, elect);
    ok = zsscanf(line2, format, nameId, elect);
    printf ("%d: |%s|%s|\n", ok, nameId, elect);
    return 0;
}

انتاج:

    2: |TVC-CCTV-0002|ELECTRICAL_TopoLine_823|
    2: |TVC-CCTV-0000||

أن تحذر، لم يتم اختبارها بالكامل ولديها قيود شديدة (الأكثر وضوحا: يقبل فقط %...s, %...c, %...[...] ويتطلب فواصل كما %...[...]; ؛ وإلا فسأعمل حقا أن أهتم بسلسلة التنسيق، بهذه الطريقة التي أهتم بها فقط %).

اضطررت لتعديل هذا الرمز قليلا للعمل بشكل صحيح:

//rm token_pure;gcc -Wall -O3 -o token_pure token_pure.c; ./token_pure 
#include <stdio.h>
#include <string.h>

int main ()
{
    char str[] = " 1 , 2 x, , 4 ";
    char *s1;
    char *s2;
    s2=(void*)&str; //this is here to avoid warning of assignment from incompatible pointer type 
        do {
            while( *s2 == ' ' || *s2 == '\t' )  s2++;
            s1 = strsep( &s2, "," );
            if( !*s1 ){
                printf("val: (empty)\n" );
            }
            else{
                int val;
                char ch;
                int ret = sscanf( s1, " %i %c", &val, &ch );
                if( ret != 1 ){
                    printf("val: (syntax error)\n" );
                }
                else{
                    printf("val: %i\n", val );
                }
            }
        } while (s2!=0 );
        return 0;
    }

والإخراج:

val: 1
val: (syntax error)
val: (empty)
val: 4

قمت بإجراء تعديل لعلامات TAB ملفات TSV المحددة، ونأمل أن تساعد:

//rm token_tab;gcc -Wall -O3 -o token_tab token_tab.c; ./token_tab 
#include <stdio.h>
#include <string.h>

int main ()
{
//  char str[] = " 1     2 x         text   4 ";
    char str[] = " 1\t 2 x\t\t text\t4 ";
    char *s1;
    char *s2;
    s2=(void*)&str; //this is here to avoid warning of assignment from incompatible pointer type 
        do {
            while( *s2 == ' ')  s2++;
            s1 = strsep( &s2, "\t" );
            if( !*s1 ){
                printf("val: (empty)\n" );
            }
            else{
                int val;
                char ch;
                int ret = sscanf( s1, " %i %c", &val, &ch );
                if( ret != 1 ){
                    printf("val: (syntax error or string)=%s\n", s1 );
                }
                else{
                    printf("val: %i\n", val );
                }
            }
        } while (s2!=0 );
        return 0;
    }

و ouput:

val: 1
val: (syntax error or string)=2 x
val: (empty)
val: (syntax error or string)=text
val: 4

هناك بعض مشاكل مع strtok () المدرجة هنا: http://benpfaff.org/writings/clc/strtok.html.

وبالتالي، فمن الأفضل تجنب strtok..

الآن، فكر في سلسلة تحتوي على حقل فارغ على النحو التالي:

char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here

يمكنك استخدام وظيفة بسيطة لتكون تحويل السلسلة في تنسيق CSV لقراءتها إلى صفيف تعويم:

int strCSV2Float(float *strFloatArray , char *myCSVStringing);

يرجى الاطلاع على الاستعمال أقل:

#include <stdio.h>
#include <stdlib.h>



int strCSV2Float(float *strFloatArray , char *myCSVStringing);

  void main()
 {

    char myCSVString[101] = "-1.4,2.6,,-0.24,1.26"; // specify input here
    float floatArr[10]; // specify size here 
    int totalValues = 0;

    printf("myCSVString == %s \n",&myCSVString[0]);

    totalValues = strCSV2Float(&floatArr[0] , &myCSVString[0]); // call the function here 

    int floatValueCount = 0;

    for (floatValueCount = 0 ; floatValueCount < totalValues ; floatValueCount++)
    {

      printf("floatArr[%d] = %f\n",floatValueCount , floatArr[floatValueCount]);

    }

 }




int strCSV2Float(float *strFloatArray , char *myCSVStringing)
{

int strLen = 0;
int commaCount =0; // count the number of commas
int commaCountOld =0; // count the number of commas
int wordEndChar = 0;
int wordStartChar = -1;
int wordLength =0;


   for(strLen=0; myCSVStringing[strLen] != '\0'; strLen++) // first get the string length
   {

       if ( (myCSVStringing[strLen] == ',')  || ( myCSVStringing[strLen+1] == '\0' ))
        {
           commaCount++;
           wordEndChar = strLen;
        }
       if ( (commaCount - commaCountOld) > 0 )
        {
          int aIter =0;
          wordLength = (wordEndChar - wordStartChar);
          char word[55] = "";
          for (aIter = 0;  aIter < wordLength; aIter++)
          {
            word[aIter] = myCSVStringing[strLen-wordLength+aIter+1];
          }

          if (word[aIter-1] == ',') 
           word[aIter-1] = '\0';

          //  printf("\n");
          word[wordLength] = '\0';
          strFloatArray[commaCount-1] = atof(&word[0]);

          wordLength = 0;
          wordStartChar = wordEndChar;
          commaCountOld = commaCount;

        }  
  }

  return commaCount;

}

انتاج | على النحو التالي :

myCSVString == -1.4,2.6,,-0.24,1.26 
floatArr[0] = -1.400000
floatArr[1] = 2.600000
floatArr[2] = 0.000000
floatArr[3] = -0.240000
floatArr[4] = 1.260000

scanf() إرجاع عدد العناصر المعينة. ربما يمكنك استخدام هذه المعلومات ...

char *data = "1, 2,,, 5, 6";
int a[6];
int assigned = sscanf(data, "%d,%d,%d,%d,%d,%d", a, a+1, a+2, a+3, a+4, a+5);
if (assigned < 6) {
    char fmt[18];
    switch (assigned) {
        default: assert(0 && "this did not happen"); break;
        case 0: fmt = ",%d,%d,%d,%d,%d"; break;
        case 1: fmt = "%d,,%d,%d,%d,%d"; break;
        case 2: fmt = "%d,%d,,%d,%d,%d"; break;
        case 3: fmt = "%d,%d,%d,,%d,%d"; break;
        case 4: fmt = "%d,%d,%d,%d,,%d"; break;
        case 5: fmt = "%d,%d,%d,%d,%d,"; break;
    }
    sscanf(data, fmt, a+(assigned<=0), a+1+(assigned<=1), a+2+(assigned<=2),
                      a+3+(assigned<=3), a+4+(assigned<=4));
}

قرف! وهذا فقط لمدة 1 مفقود القيمة
كما تم الإشارة إليها من خلال إجابات أخرى، فأنت أفضل حالا في تحليل السلسلة بطريقة "المعتادة": fgets() و strtok().

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top