Skocz do zawartości

Rozbicie stringa na 2 zmienne liczbowe


Robert85

Pomocna odpowiedź

Czołem,

jako osoba mniej ścisła, męczę się od wczoraj z próbą ustawienia dwóch zmiennych pobranych z pliku txt zlokalizowanym na zdalnym serwie, korzystając z biblioteki HTTPClient.h.

Plik tekstowy ma zawartość "640, 1530" (ale może być też dowolny separator lub nawet w osobnej linii, mam dostęp do tego pliku i mogę go zrobić jak chcę, wazne aby działało). 
 

http.begin("http://192.168.1.2/zmienne.txt");

int httpCode2 = http.GET(); 
  if (httpCode2 > 0) { //Check for the returning code
                String payload2 = http.getString();
  }
 else {
                Serial.println("<br>Blad pobrania danych z serwa</br>");
                 }
 
             http.end(); 


Za cholerę nie potrafię przerobić tych dwóch wartości na 2 osobne zmienne liczbowe czyli np.:
zmienna1 = 640;
zmienna2 = 1530;
Kombinowałem i przerobieniem tego na tablicę ale wówczas zmienna[0] = 640 a zmienna[1] = 0. Próbowałem używać funkcji konwertujacych string na int, ale rezultaty podobne. 
Większość przykładów na necie to operowanie pobieranym plikiem z np. karty SD i użycie funkcji fscanf, ale  przypadku pobrania zawartości pliku przez http.GET() to już nie działa. 
Myślałem też o przerobieniu tego na XML, ale to bez sensu skoro to tylko 2 liczbowe wartości.
Będę wdzięczny za naprowadzenie. 

Pozdro

Link do komentarza
Share on other sites

[C++] https://en.cppreference.com/w/cpp/regex
[C] https://stackoverflow.com/questions/1085083/regular-expressions-in-c-examples

Więcej raczej nie muszę tłumaczyć, ewentualnie skanujesz stringa jako tablicę znaków dopisując znaki do tablicy A i po wystąpieniu separatora przetwarzasz tablicę A na zmienną i ją czyścisz.

Link do komentarza
Share on other sites

(edytowany)

Znalazłem taką funkcję i staram się to zrobić za jej pomocą. 

 

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

int main(void){
    char *input = "640 1530"; 
    int len = 0;

    sscanf(input, "%*[^0-9]%n", &len);//count not-digits(The Number isn't negative)

    char *p = input + len;
    char *start = p;
    int v, n = 0;
    while(1 == sscanf(p, "%d%n", &v, &len)){
        ++n;//count elements
        p += len;
    }
    int array[n];//or allocate by malloc(and free)
    char *endp = NULL;
    int i;
    for(i = 0; i < n; ++i){
        array[i] = strtol(start, &endp, 10);
        start = endp + 1;
    }
    //check print
    for(i = 0; i < n; ++i)
        printf("%d ", array[i]);
    puts("");
    return 0;
}

 

Edytowano przez Robert85
Link do komentarza
Share on other sites

Podam Ci sposób który ja stosuję.

Niech napis wejściowy będzie po prostu ciągiem znaków (czyli jakimś kawałkiem pamięci, w którym po kolei siedzą sobie znaczki) i dysponujemy adresem pierwszego elementu (wskaźnikiem).

Czyli przykładowo:

char *napis="640, 1530";

ale równie dobrze może to być:

char *napis = "  640 : 1530 : ";

Spróbuję znaleźć pierwszą liczbę w napisie. W tym celu przeszukuję napis aż do napotkania końca napisu lub początku liczby:
 

bool blad;
char *wskaznik;
int zmienna;

wskaźnik = napis;
blad = false;

while (1) {
  if (!*wskaznik) { // czy to koniec napisu?
    blad = true; // niedobrze!
    break;
  }
  if (isdigit(*wskaznik)) { // czy to cyfra?
    break;
  }
  if (*wskaznik == '-' && isdigit(wskaznik[1]) { // a może początek liczby ujemnej?
    break;
  }
  wskaznik++; // nie, to jeszcze nie liczba, sprawdzimy następny znak
}

// i w tym miejscu mamy albo początek, liczby, albo koniec napisu
if (blad) {
   // tu sobie wymyśl co robić jeśli napis nie zawiera liczby
}

// funkcja pobiera z pierwszego parametru liczbę. Drugim parametrem jest
// adres zmiennej, do której wstawia adres następnego znaku za liczbą.
// Trezeci to po prostu podstawa - jako że używamy systemu dziesiętnego
// podajemy funkcji 10

zmienna = strtol(napis, &napis, 10);
// i tu mamy w zmiennej wartość pierwszej liczby

Wygląda na skomplikowane? Jak się przypatrzysz, to wcale nie.

Jak wyciągnąć dwie liczby powinieneś na tej podstawie sam napisać, jakby Ci się nie chciało to masz tu przykład:

#define ILOSC_LICZB 2

bool blad;
char *wskaznik;
int zmienna[ILOSC_LICZB];

wskaźnik = napis;
blad = false;
int i;

for (i=0; i< ILOSC_LICZB; i++) { // pobieramy każdą liczbę
  while (1) {
    if (!*wskaznik) { // czy to koniec napisu?
      blad = true; // niedobrze!
      break;
    }
    if (isdigit(*wskaznik)) { // czy to cyfra?
      break;
    }
    if (*wskaznik == '-' && isdigit(wskaznik[1]) { // a może początek liczby ujemnej?
      break;
    }
    wskaznik++; // nie, to jeszcze nie liczba, sprawdzimy następny znak
  }
  // i w tym miejscu mamy albo początek, liczby, albo koniec napisu
  if (blad) {
    break; // nie było którejś kolejnej liczby, przerywamy pracę
  }
  zmienna[i] = strtol(napis, &napis, 10);
}

// i tu mamy albo tablicę zmienna wypełnioną naszymi liczbami, albo zasygnalizowany błąd

I co ważne:

funkcje isdigit i strtol są częścią standardu C a nie Arduino, w związku z tym należy zapowiedzieć kompilatorowi że będziemy je stosować, W tym celu trzeba w pliku źródłowym wstawić na początku dwie dyrektywy:

// tu siedzą isdigit, isalpha, isalnum, islower i inne
// ciekawe funkcje operujące na pojedynczych znakach ASCII

#include <ctype.h>
  
// a tu siedzą standardowe funkcje języka C, m.in. strtol
  
#include <stdlib.h>
  
2 godziny temu, H1M4W4R1 napisał:

Naprawdę, nie znasz prostszych sposobów? No to ja Twojej gry nie kupię bo mi się procek w telefonie usmaży 😉

Edytowano przez ethanak
  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

2 minuty temu, ethanak napisał:

Naprawdę, nie znasz prostszych sposobów? No to ja Twojej gry nie kupię bo mi się procek w telefonie usmaży

Uwaga lekki offtopic - tylko do @ethanak 😉 

To jest najprostszy sposób, bo najmniej problemów. A pod regexami był alternatywny 😛 I rozumiem, że masz jednordzeniowy procesor o taktowaniu 16MHz w telefonie? (sarkazm)... W moich aplikacjach praktycznie takich rzeczy nie widać, bo są liczone w tle (o ile nie są hardcodowane...)

Najpierw gry są tworzone pod CTR, gdzie tylko ma "działać" (równie dobrze nie musi) w edytorze. Celem jest nagranie reklamy, a nie tworzenie w pełni wydajnej i funkcjonalnej gry. To drugie się nie opłaca.

Tutaj przykład rozwiązania problemu z języka C# - porównaj sobie ile linijek zajmuje to u Ciebie, a ile u mnie 😉 - w mojej branży czas jest cenniejszy niż jakakolwiek wydajność (powód powyżej)

return await (await RegEx.GetMatchesAsync("<regex_string>")).SelectAsync(q => int.Parse(q));

A tyle wpisuję w edytorze

 (ret aw (aw rex.GMAs("<regex_string>") 🠒 .SAs(q => int.Pa(q); 

Większość programistów marnuje zasoby procesora... Wiem po tym jak dostaję projekty po innych osobach - najczęściej wszystko stoi na jednym wątku przez co gra chodzi jak muł... Po coś jednak telefon ma te osiem rdzeni, a system zadowoli się jednym...

A jak już coś ma być wydane:

Najpierw ma działać, optymalizacją zajmie się stażysta.

Link do komentarza
Share on other sites

Dziękuję za pomoc. I tak rozbiłem się o mur typu zmiennych.
Biblioteka HTTPClient ma funkcję pobierającą dane z URLa do zmiennej typu string
 

String payload2 = http.getString();


Funkcja, którą wrzuciłem działa bdb, ale jeśli zmienna jest char * a nie String.
Kombinowałem z konwersją string na const char itd, ale poległem. 
Muszę chyba się przewietrzyć bo mi zwoje w mózgu przegrzało. Kiedyś (z 15 lat temu) robiłem jakieś proste wizytówki w php i tam nie miałem nigdy problemu z typami zmiennych a tutaj rozbijam się na banałach, aż mi wstyd. 

Program na ten moment wygląda tak (brakuje tylko przekonwertowania zmiennej payload2 na czas1 (mimo wielu podejść - nie poradziłem sobie).
Na tem moment  nagłówku mam to:
#include <NTPClient.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include <HTTPClient.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
 

                HTTPClient http;
 
                http.begin("http://192.168.1.2/zmienne.txt"); //adres url ze zmiennymi 
				// zawartość pliku txt to "640 1530"
                
                int httpCode2 = http.GET(); //Make the request
                if (httpCode2 > 0) { //Check for the returning code
                String payload2 = http.getString(); // tutaj mam zawartość pliku txt  postaci "640 1530"
                
// miejsce na przekonertowanie String payload2 na char *czas1 // 

                    int len = 0;
                    sscanf(czas1, "%*[^0-9]%n", &len);
                     char *p = czas1 + len;
                     char *start1 = p;
                     int v, n = 0;
                     while(1 == sscanf(p, "%d%n", &v, &len)){
                     ++n;
                      p += len;
                      }
                      int array[n];
                      char *endp = NULL;
                      int i;
                      for(i = 0; i < n; ++i){
                      array[i] = strtol(start1, &endp, 10);
                      start = endp + 1;
                       }
                      int zmienna1 = array[0];
                      int zmienna2 = array[1];

 

Link do komentarza
Share on other sites

Tak sobie patrzę na to asynchroniczne parsowanie prostego stringa i dochodzę do wniosku że jednak apple niesłusznie był oskarżany o celowe spowalnianie swoich urządzeń.

A tymczasem facebook chyba czyta w moich myślach - po pokazał mi obrazek, którym muszę się podzielić. Idealnie pasuje do tej dyskusji:

dev.thumb.jpg.784de3c2ca4367c075797df2f06ba0d2.jpg

  • Lubię! 1
Link do komentarza
Share on other sites

26 minut temu, H1M4W4R1 napisał:

W moich aplikacjach praktycznie takich rzeczy nie widać, bo są liczone w tle

No wiesz, propozycja użycia regexpa tam gdzie praktycznie wystarczy strpbrk świadczy o tym, że być może paru innych rzeczy nie widać 🙂

(nie obrażaj się, jakbyś nie wiedział to jest to żart)

Link do komentarza
Share on other sites

Dodałem zgodnie z sugestią:

 

                String payload2 = http.getString();
                 (char*)&payload2;
                    int len = 0;
                     sscanf(payload2, "%*[^0-9]%n", &len);

ale i  tak wypluwa błąd:

cannot convert 'String' to 'const char*' for argument '1' to 'int sscanf(const char*, const char*, ...)'

 

Link do komentarza
Share on other sites

Scanf(char*) & payload.... 

Spróbuj też bez ampersand. Sorki pisząc z komórki nie mogę tego precyzyjnie przedstawić 

Edytowano przez _LM_
Link do komentarza
Share on other sites

2 minuty temu, Robert85 napisał:

Dodałem zgodnie z sugestią

String payload2 = http.getString();
(char*)&payload2;
sscanf(payload2, "%*[^0-9]%n", &len);

A co to niby miało znaczyć?

Bo znaczy to w tej chwili tyle:

  1. Pobierasz do zmiennej payload2 typu String wynik http.getString();
  2. Bierzesz teraz payload2, przekształcasz na char *, a wynik tego przekształcenia wyrzucasz.
  3. Wywołujesz funkcję sscanf, której pierwszym argumentem jest payload2 (typu String, bo się przecież magicznie nie zmienił)
  4. Dziwisz się, że kompilator nie chce String tam gdzie chce const char *.

Na pewno taka była sugestia?

A czemu nie np.:

sscanf(payload2.c_str(), "%*[^0-9]%n", &len);

(czyli zgodnie z sugestią)?

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.