Sprawdzenie czy ciąg znaków składa się z liczb oddzielonych separatorem

0

Cześć wszystkim.

Otóż w zmiennej typu char znajduje się jakiś tekst. Chciałbym go porównać tak aby był prawidłowy według określonej sekwencji znaków. Chodzi o to, że ma być sprawdzenie czy tekst jest w formie takiej: |10|20|3|100| . Najważniejsze jest to, żeby były prawidłowe tylko dwa znaki | a pomiędzy nimi liczba dziesiętna. Takich ciągów znaków może być nieskończenie wiele np: |1|40|100|50|6|50|n|n|n|n|n| Dodam jeszcze, że pomiędzy znakami | mogą być tylko maksymalnie 3 cyfry. Udało mi się wyskrobać takie coś jak poniżej. Jest to rozwiązanie dla jednego znaku pomiędzy sekwencją | czyli |2|3| . Meczę się aby zrobić to dla 3 znaków, ale tak żeby można było sprawdzić również jeden znak lub dwa czyli np: |2|10|200| lub |20|5|100| itp. Tracę powoli pomysły. Co piszę kod to go zmazuje lub nie działa. Co zrobić aby zadziałało tak jak tego chcę? Wyrażenia regex nie wchodzą w grę ponieważ moje środowisko pracy tego nie obsługuje

#include "stdarg.h"
#include "string.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

bool validate(char* tab)
{
    bool isNumericMode = false;
    bool lastNumericCheck = false;
    char* ptr = tab;
    while (*ptr != 0) 
    {
        if (!isNumericMode) {
            if (*ptr != '|') return false;
            isNumericMode = true;
            lastNumericCheck = true;
        } else {
            lastNumericCheck = false;
            if (isdigit(*ptr)) isNumericMode = false;
            else return false;
        }
      ptr++;
    }
    return lastNumericCheck;
}

int main()
{
    char cmd[] = "|5|3|";
    printf("%d", validate(cmd));
    
}
1

Okej, czyli chcesz sprawdzić, nazwijmy to po ludzku, że dany ciąg znaków składa sie jedynie z cyfr 0..9, oddzielonych od siebie separatorem |, przy czym poszczególne elementy mogą mieć długość większą niż 0, mniejszą niż 4 (bo nie może być pewnie || obok siebie)? I zakładam że cały ciąg musi (?) zaczynać się i kończyć tym separatorem? Więc np ciąg |1| jest poprawny, ale |1 oraz 1| już nie? Pytanie czy dla Ciebie np znak | również jest poprawny? Albo ""?

Możesz to zrobić albo iterując po znakach, albo używając kolekcji.

Jeśli chciałbyś użyć kolekcji, to możesz zesplitować ciąg znaków na vector(), i sprawdzić potem czy każdy z elementów vectora pasuje do kryteriów.

0
Riddle napisał(a):

Okej, czyli chcesz sprawdzić, nazwijmy to po ludzku, że dany ciąg znaków składa sie jedynie z cyfr 0..9, oddzielonych od siebie separatorem |, przy czym poszczególne elementy mogą mieć długość większą niż 0, mniejszą niż 4 (bo nie może być pewnie || obok siebie)? I zakładam że cały ciąg musi (?) zaczynać się i kończyć tym separatorem? Więc np ciąg |1| jest poprawny, ale |1 oraz 1| już nie? Pytanie czy dla Ciebie np znak | również jest poprawny? Albo ""?

Tak nie może być obok siebie || ponieważ w środku nie ma liczby dziesiętnej więc jest źle. Tak samo nie może być |1 oraz 1|. Prawidłowe znaki jakie powinny być to tylko: |, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 Ciąg musi się kończyć oraz zaczynać znakiem |

Możesz to zrobić albo iterując po znakach, albo używając kolekcji.

Jeśli chciałbyś użyć kolekcji, to możesz zesplitować ciąg znaków na vector(), i sprawdzić potem czy każdy z elementów vectora pasuje do kryteriów.

Niestety vectory odpadają. CubeIDE nie posiada biblioteki vector

1

jak rozumiem masz 4 stany, w które może wpaść parser:

  • początek: oczekujesz |
  • ostatnia była cyfra: oczekujesz kolejnej cyfry (ale maksymalnie 3 cyfry) albo |
  • ostatni był | : oczekujesz cyfry
  • koniec: oczekujesz |

I z tej strony bym do tego podszedł.

0
RockySQL napisał(a):
Riddle napisał(a):

Okej, czyli chcesz sprawdzić, nazwijmy to po ludzku, że dany ciąg znaków składa sie jedynie z cyfr 0..9, oddzielonych od siebie separatorem |, przy czym poszczególne elementy mogą mieć długość większą niż 0, mniejszą niż 4 (bo nie może być pewnie || obok siebie)? I zakładam że cały ciąg musi (?) zaczynać się i kończyć tym separatorem? Więc np ciąg |1| jest poprawny, ale |1 oraz 1| już nie? Pytanie czy dla Ciebie np znak | również jest poprawny? Albo ""?

Możesz to zrobić albo iterując po znakach, albo używając kolekcji.

Jeśli chciałbyś użyć kolekcji, to możesz zesplitować ciąg znaków na vector(), i sprawdzić potem czy każdy z elementów vectora pasuje do kryteriów.

Tak nie może być obok siebie || ponieważ w środku nie ma liczby dziesiętnej więc jest źle. Tak samo nie może być |1 oraz 1|. Prawidłowe znaki jakie powinny być to tylko: |, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 Ciąg musi się kończyć oraz zaczynać znakiem |

No to pojawia się pytanie jak u Ciebie powinna wyglądać pusta lista liczb? No chyba w takim razie "|", tak? Bo "||" byłoby jednoelementową listą z cyfrą o 0-owej długości.

Oprócz iteracji, tak jak pokazywał @LukeJL i vectorów, możesz jeszcze użyć wyrażeń regularnych, które są deklaratywne. Wyglądałoby to tak

0
LukeJL napisał(a):

jak rozumiem masz 4 stany, w które może wpaść parser:

  • początek: oczekujesz |
  • ostatnia była cyfra: oczekujesz kolejnej cyfry albo |
  • ostatni był | : oczekujesz cyfry
  • koniec: oczekujesz |

I z tej strony bym do tego podszedł.

No to ma takie coś mniej więcej w tej części kodu:

        if (!isNumericMode) {
            if (*ptr != '|') return false;
            isNumericMode = true;
            lastNumericCheck = true;
        } else {
            lastNumericCheck = false;
            if (isdigit(*ptr)) isNumericMode = false;
            else return false;

Na początku szukam znaku | Jeśli go znajdę to przełączam się w tryb szukania cyfry. Największa moja zagwostka to jak znaleźć te dwie brakujące cyfry

1
RockySQL napisał(a):
LukeJL napisał(a):

jak rozumiem masz 4 stany, w które może wpaść parser:

  • początek: oczekujesz |
  • ostatnia była cyfra: oczekujesz kolejnej cyfry albo |
  • ostatni był | : oczekujesz cyfry
  • koniec: oczekujesz |

I z tej strony bym do tego podszedł.

No to ma takie coś mniej więcej w tej części kodu:

        if (!isNumericMode) {
            if (*ptr != '|') return false;
            isNumericMode = true;
            lastNumericCheck = true;
        } else {
            lastNumericCheck = false;
            if (isdigit(*ptr)) isNumericMode = false;
            else return false;

Na początku szukam znaku | Jeśli go znajdę to przełączam się w tryb szukania cyfry. Największa moja zagwostka to jak znaleźć te dwie brakujące cyfry

Na samych booleanach tego nie zrobisz, bo musisz zapamiętać ilość wczytanych cyfr.

1

zmodyfikowałem twój kod z pierwszego posta i coś takiego mi się wykluło:

bool validate(char* ptr)
{
    if (*ptr != '|') return false;
    ptr++;
    bool isNumericMode = true;    
    int digitCount = 0;
    while (*ptr != 0) 
    {
        if (isNumericMode) {
            if (*ptr == '|') {
                isNumericMode = false;
            } else {
                digitCount++;                
                if (!isdigit(*ptr) || digitCount > 3) return false;
            }
        } else {
            if (!isdigit(*ptr)) return false;
            isNumericMode = true;
            digitCount = 0;
        }
      ptr++;
    }
    if (*(ptr-1) != '|') return false;
    return true;
}

czyli sprawdzanie czy pierwszy znak to |
potem pętla i jeśli jesteśmy w stanie liczba to oczekujemy albo |, który zmienia stan, albo oczekujemy cyfry (ale nie więcej niż 3).
jeśli jesteśmy w stanie |, to oczekujemy cyfry. (Zakładam, że muszą być jakieś liczby pomiędzy tymi pionowymi kreskami i || byłoby nieprawidłowym zapisem)
a na końcu sprawdzamy, czy ostatni znak to |

0
LukeJL napisał(a):

zmodyfikowałem twój kod z pierwszego posta i coś takiego mi się wykluło:

bool validate(char* ptr)
{
    if (*ptr != '|') return false;
    ptr++;
    bool isNumericMode = true;    
    int digitCount = 0;
    while (*ptr != 0) 
    {
        if (isNumericMode) {
            if (*ptr == '|') {
                isNumericMode = false;
            } else {
                digitCount++;                
                if (!isdigit(*ptr) || digitCount > 3) return false;
            }
        } else {
            if (!isdigit(*ptr)) return false;
            isNumericMode = true;
            digitCount = 0;
        }
      ptr++;
    }
    if (*(ptr-1) != '|') return false;
    return true;
}

czyli sprawdzanie czy pierwszy znak to |
potem pętla i jeśli jesteśmy w stanie liczba to oczekujemy albo |, który zmienia stan, albo oczekujemy cyfry (ale nie więcej niż 3).
jeśli jesteśmy w stanie |, to oczekujemy cyfry. (Zakładam, że muszą być jakieś liczby pomiędzy tymi pionowymi kreskami i || byłoby nieprawidłowym zapisem)
a na końcu sprawdzamy, czy ostatni znak to |

Oooo o to chodziło. Dziękuję za pomoc. Patrzę na kod i już wiem, że trochę źle kombinowałem. Życie mi uratowałeś. Jeszcze raz wielkie dzięki :D

0

chociaż w mojej wersji za prawidłowe zostają łańcuchy "||" oraz "|", ale tutaj można by dodać dodatkowy warunek, który by sprawdzał, czy w ogóle są tam jakieś liczby (np. przy znalezieniu pierwszej liczby flaga ustawiona na true). Albo można by sprawdzić czy długość całego stringa jest większa od 2, bo tylko wtedy jest szansa, że są tam liczby w środku, jeśli na początku i końcu ma być |
EDIT w zasadzie wystarczy sprawdzanie digitCount

bool validate(char* ptr)
{
    if (*ptr != '|') return false;
    ptr++;
    bool isNumericMode = true;    
    int digitCount = 0;
    while (*ptr != 0) 
    {
        if (isNumericMode) {
            if (*ptr == '|') {
                isNumericMode = false;
            } else {
                digitCount++;                
                if (!isdigit(*ptr) || digitCount > 3) return false;
            }
        } else {
            if (!isdigit(*ptr)) return false;
            isNumericMode = true;
            digitCount = 1; // TUTAJ ZMIENIŁEM
        }
      ptr++;
    }
    if (*(ptr-1) != '|' || digitCount == 0 /* TUTAJ DODAŁEM */ ) return false;
    return true;
}
0
Riddle napisał(a):

Na samych booleanach tego nie zrobisz, bo musisz zapamiętać ilość wczytanych cyfr.

W ogóle temat jest z tych, że pełne prztworzenie (pozyskanie liczb) nie jest ani trudniejsze, ani nie bardziej kosztowne od "sprawdzenia"

1
#include <iostream>
#include <map>
using namespace std;

bool chceck_sequence(const string &str,char separator='|')
{
	enum state { start,separ,digit1,digit2 };
	static const map<pair<state,int>,state> tb
	{
		{ make_pair(start,  2), separ  },
		{ make_pair(separ,  3), digit1 },
		{ make_pair(digit1, 2), separ  },
		{ make_pair(digit1, 3), digit2 },
		{ make_pair(digit2, 2), separ  },
		{ make_pair(digit2, 3), start  },
	};
	state curr=start;
	try { for(char ch:str) curr=tb.at(make_pair(curr,(ch==separator?2:0)|(isdigit(ch)?3:0))); }
	catch(...) { return false; }
	return (curr==separ);
}

int main()
{
	cout<<chceck_sequence("|1|40|100|50|6|50|")<<endl;
	cout<<chceck_sequence("|1|40|1002|50|6|50|")<<endl;
	cout<<chceck_sequence("|1|40|100|50|6|50")<<endl;
	cout<<chceck_sequence("1|40|100|50|6|50|")<<endl;
	cout<<chceck_sequence("|1||40|100|50|6|50|")<<endl;
	cout<<chceck_sequence("|1|40|1OO|50|6|50|")<<endl;
	return 0;
}
1
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

enum state {
    START,
    AFTER_PIPE,
    AFTER_1DIGIT,
    AFTER_2DIGIT,
    AFTER_3DIGIT,
    ERROR,
};

bool ispipe(char c) {
    return c == '|';
}

bool validate(char* tab)
{
    enum state s = START;
    char *p = tab;
    while (*p != 0) {
        switch (s) {
        case START:
            s = ispipe(*p) ? AFTER_PIPE : ERROR;
            break;
        case AFTER_PIPE:
            s = isdigit(*p) ? AFTER_1DIGIT : ERROR;
            break;
        case AFTER_1DIGIT:
            s = isdigit(*p) ? AFTER_2DIGIT : ispipe(*p) ? AFTER_PIPE : ERROR;
            break;
        case AFTER_2DIGIT:
            s = isdigit(*p) ? AFTER_3DIGIT : ispipe(*p) ? AFTER_PIPE : ERROR;
            break;
        case AFTER_3DIGIT:
            s = ispipe(*p) ? AFTER_PIPE : ERROR;
            break;
        case ERROR:
            break;
        }
        p++;
    }
    return s == AFTER_PIPE;
}

int main()
{
    char cmd1[] = "|5|3|";
    char cmd2[] = "|5||";
    char cmd3[] = "|5|443|434|";
    char cmd4[] = "|5|443|434";
    char cmd5[] = "|5|443|434|1000|";
    printf("%d %d %d %d %d\n", validate(cmd1), validate(cmd2), validate(cmd3), validate(cmd4), validate(cmd5));
    
}

Podobne podejście co @_13th_Dragon , tylko trochę bardziej ręczne, i trochę bardziej w C (napisałem, zanim przeczytałem cały wątek).

0

A próbowałeś tak ? - tworzysz 3 struktury -

struct jeden{ char znak;} 
struct dwa{ char znak[2];} 
struct trzy{char znak[3]} 

char tablica[11]={"0|32|432|4"};
jeden *tmp;
dwa *tmp2;
trzy *tmp3;

char *tst,*tst2; //wskaźnik do jeżdzenia po tablicy
void main()
for(int tst=&tablica[0];tst<sizeof(tablica);tst++){

if(*tst==39/* czy jaki tam jest ten kod ascii znaku "|" */){
tst2=tst;tst2++;
tmp=tst2;tmp2=tst2;tmp3=tst2;
// może trzeba zamienić strukturę na liczbę
if((*tmp>=0 && tmp<1000) || (*tmp2>=0 && tmp<1000) || (*tmp3>=0 && tmp<1000)){printf("%s","to jest liczba");}
}
}
}

Może trzeba przy sprawdzaniu rzutować na int. Może przydałaby się funkcja zamieniająca zera i jedynki na liczbę,
Wtedy tworzysz tablice typy int 2,4,8,16,...może do rozmiaru 4096 może większa (przyda Ci się jak coś takiego będziesz miał zawsze pod ręką - chyba na to mówią "warsztat programisty") i jedziesz pętlą po bitach podnosząc je do potęgi 2 coraz to większej.
I masz funkcję do zamiany każdej wartości na zera i jedynki i na odwrót, możesz sobie używać typów np. 11 bitowych.
No ale to zależy czy to ma być signed czy unsigned (ze znakiem czy bez).
Trochę mniej bibliotek wymaga niż kod powyżej. xD

0

Ostatnio o C pytałeś rok temu. Nie interesowałem się chyba zbyt tym językiem.

Nie wczytywałem się w wątek, ale rozwiązałem to specjalnie troszkę inaczej niż inni,
Zasadniczo jest to gorsze rozwiązanie ale może okazać się pouczające. Zachęcam do nauki !

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

void fail( void )  {

  puts( "Błąd" );
  exit( EXIT_FAILURE );
	
}

int main( void ) {
	
	char text[] = "|11|299|";
	
	if( strstr( text, "||") != NULL )  fail();
	if( ! strcmp( text, "|" ) ) fail();
	if( text[0] != '|' )  fail();
	if( text[ strlen( text ) - 1 ] != '|' )  fail();
	
	char *tp = strtok( text, "|" );
	do {
	
	  if( strlen( tp ) > 3 )  fail();
	  for( size_t i = 0; tp[i] != '\0'; i++ )
	    if( ! isdigit( tp[i] ) )  fail();
	
	} while ( ( tp = strtok( NULL, "|" ) ) != NULL );
	
	puts( "ok" );
	return 0;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1