Jak sformatować liczbę od 1123456789 do 1123456789 w C?

83

Jak mogę w języku C formatować liczbę od 1123456789do 1,123,456,789? Próbowałem użyć, printf("%'10d\n", 1123456789);ale to nie działa.

Czy mógłbyś coś doradzić? Im prostsze rozwiązanie, tym lepiej.

goe
źródło
1
Tylko do Twojej wiadomości: flaga „separator tysięcy” dla printf()rodziny sformatowanych funkcji we / wy (znak pojedynczego cudzysłowu: „) to niestandardowa flaga obsługiwana tylko w kilku implementacjach bibliotek. Szkoda, że ​​to nie jest standard.
Michael Burr,
1
To zależy od lokalizacji. Zgodnie ze stroną podręcznika Linux , wygląda na LC_NUMERIC. Jednak nie wiem, które locale to obsługuje.
Joey Adams
1
@Joey, ustawienie LC_NUMERIClocale na aktualne ""sprawia, że 'działa na moim Macu i na komputerze z Linuksem, który właśnie sprawdziłem.
Carl Norum,
Należy zauważyć, że wersje printf()rodziny funkcji POSIX 2008 (2013) standaryzują użycie 'znaku (pojedynczego cudzysłowu lub apostrofu) ze specyfikacjami konwersji formatowania liczb dziesiętnych, aby określić, że liczba powinna być sformatowana z separatorami tysięcy.
Jonathan Leffler
2
Zauważ również, że w domyślnym "C"ustawieniu narodowym niepieniężny separator tysięcy jest niezdefiniowany, więc "%'d"w "C"ustawieniach regionalnych nie zostaną utworzone przecinki . Musisz ustawić ustawienia regionalne z odpowiednim niepieniężnym separatorem tysięcy. Często setlocale(LC_ALL, "");wykona zadanie - inne wartości dla nazwy locale (inne niż pusty łańcuch) są zdefiniowane jako implementacja.
Jonathan Leffler

Odpowiedzi:

83

Jeśli twój printf obsługuje 'flagę (zgodnie z wymaganiami POSIX 2008 printf()), prawdopodobnie możesz to zrobić po prostu ustawiając odpowiednio swoje locale. Przykład:

I buduj i uruchamiaj:

Przetestowano na Mac OS X i Linux (Ubuntu 10.10).

Carl Norum
źródło
1
Przetestowałem to sprintf()w systemie wbudowanym i nie działa (oczywiście, ponieważ jak mówisz, nie obsługuje flagi „.
gbmhunter
Jestem pewien, że możesz znaleźć bibliotekę C, która by to obsługiwała bez większych problemów.
Carl Norum
Rzuciłem okiem, nie znalazłem nic odpowiedniego i wdrożyłem własny, korzystając z niektórych z powyższych pomysłów. Byłoby wspaniale znaleźć rzeczywistą bibliotekę, aby można było jej używać na liczbach zmiennoprzecinkowych i łańcuchach z miejscami dziesiętnymi.
gbmhunter
1
Wydaje się tragicznie , że wbudowany system FWIW AtmelStudio printf () nie obsługuje 'modyfikatora. Z nagłówka: Copyright ... 2007 Joerg Wunsch ... 1993 Regents of the University of Californiatj. Pochodna BSD.
Bob Stein
2
Chociaż jest to przydatne - niekoniecznie chcesz zmienić stan tej funkcji (setlocale).
ideasman42
46

Możesz to zrobić rekurencyjnie w następujący sposób (uważaj, INT_MINjeśli używasz dopełnienia do dwóch, będziesz potrzebować dodatkowego kodu do zarządzania tym):

Podsumowanie:

  • Użytkownik wywołuje printfcommaliczbę całkowitą, specjalny przypadek liczb ujemnych jest obsługiwany przez proste wypisanie "-" i uczynienie liczby dodatnią (jest to bit, który nie zadziałaINT_MIN ).
  • Kiedy wchodzisz printfcomma2 liczba mniejsza niż 1000 zostanie po prostu wydrukowana i zwrócona.
  • W przeciwnym razie rekursja zostanie wywołana na wyższym poziomie (tak więc 1234,567 zostanie wywołane z 1,234, a następnie 1), aż zostanie znaleziona liczba mniejsza niż 1000.
  • Następnie ta liczba zostanie wydrukowana, a my przejdziemy z powrotem w górę drzewa rekurencji, drukując przecinek i następną liczbę.

Istnieje również bardziej zwięzła wersja, chociaż wykonuje niepotrzebne przetwarzanie podczas sprawdzania liczb ujemnych na każdym poziomie (nie ma to znaczenia, biorąc pod uwagę ograniczoną liczbę poziomów rekursji). Ten jest kompletnym programem do testowania:

a wynik to:


Iteracyjne rozwiązanie dla tych, którzy nie ufają rekursji (chociaż jedynym problemem związanym z rekurencją jest zwykle miejsce na stosie, które nie będzie tutaj problemem, ponieważ będzie to tylko kilka poziomów głębokości, nawet dla 64-bitowej liczby całkowitej):

Oba generują 2,147,483,647dla INT_MAX.


Cały powyższy kod dotyczy grup trzycyfrowych oddzielonych przecinkami, ale możesz też użyć innych znaków, takich jak spacja:

paxdiablo
źródło
Myślę, że powinno się to raczej rozwiązać iteracyjnie, ponieważ problem jest bardziej naturalnie iteracyjny („oddziel co trzecią cyfrę”) niż rekurencyjny („oddziel trzecią cyfrę od reszty, a następnie powtórz to na pozostałych”).
Joren
Sugerowana poprawka dla MIN_INT: zmień printfcomma2, aby pobierał int bez znaku. Otóż ​​to. Niezbyt dużo „dodatkowego kodu” :-)
Steve Jessop
@Joren: Dodałem rozwiązanie iteracyjne i do pewnego stopnia pokazuje, dlaczego rozwiązanie rekurencyjne ma wartość. Chociaż w wielu przypadkach unikanie rekursji jest kwestią standardów kodowania.
Clifford
@Steve: Tylko zmieniając typ argumentu nie rozwiąże, bo UB został wywołany jak tylko negować nw printfcomma. Musisz wymusić konwersję na unsigned przed jej zanegowaniem.
R .. GitHub STOP HELPING ICE
1
@Nehal, nie zaczyna się od nowa w tym sensie, że cały bieżący postęp zostanie utracony. Wywołuje się rekurencyjnie, a następnie powraca do następnej instrukcji, czyli printf.
paxdiablo
11

Oto bardzo prosta implementacja. Ta funkcja nie zawiera sprawdzania błędów, rozmiar bufora musi zostać zweryfikowany przez wywołującego. Nie działa również dla liczb ujemnych. Takie ulepszenia pozostawia się czytelnikowi jako ćwiczenie.

Greg Hewgill
źródło
Podoba mi się ten, używa sprintf zamiast printf, co jest przydatne w systemach wbudowanych.
gbmhunter
1
Całkiem fajnie, ale wymaga pewnych drobnych poprawek, aby działać na liczbach ujemnych.
ideasman42
(zmodyfikowana wersja dla obsługi liczb ujemnych stackoverflow.com/a/24795133/432509 )
ideasman42
5

Egads! Robię to cały czas, używając gcc / g ++ i glibc na Linuksie i tak, operator 'może być niestandardowy, ale podoba mi się jego prostota.

Daje wynik:

Duża liczba: 12,345,678

Wystarczy zapamiętać wywołanie „setlocale”, w przeciwnym razie nic nie sformatuje.

lornix
źródło
2
Niestety wydaje się, że to nie działa w systemie Windows / gcc 4.9.2.
rdtsc
Cóż, Drat! Pomyślałbym, że gcc na dowolnej platformie da podobne wyniki niezależnie od systemu operacyjnego. Przypuszczam, że dobrze wiedzieć, ale zastanawiam się, dlaczego. Hmmmmm .....
lornix
Zauważ, że jeśli używana biblioteka C nie obsługuje 'flagi, to nie otrzymasz żądanego wyniku - i jest to niezależne od kompilatora. Kompilator zapewnia, że ​​funkcja biblioteki dla printf()jest wywoływana za pomocą ciągu formatu; interpretacja tego zależy od funkcji biblioteki. W systemie Windows jest całkowicie możliwe, że biblioteka CRT nie zapewnia potrzebnego wsparcia - i nie ma znaczenia, którego kompilatora używasz.
Jonathan Leffler
3

Być może wersja uwzględniająca locale byłaby interesująca.

Ma to błąd (ale uważam go za dość niewielki). Na sprzęcie z dopełnieniem do dwóch nie przekształci poprawnie liczby najbardziej ujemnej, ponieważ próbuje zamienić liczbę ujemną na jej równoważną liczbę dodatnią z N = -N;uzupełnieniem do dwóch, maksymalna liczba ujemna nie ma odpowiadającej liczby dodatniej, chyba że promować go do większego typu. Jednym ze sposobów obejścia tego jest promowanie liczby odpowiadającej typowi bez znaku (ale jest to nieco nietrywialne).

Jerry Coffin
źródło
Zadałem pytanie skierowane bardziej na wieloplatformową implementację formatu '-flag tutaj: stackoverflow.com/q/44523855/2642059 Myślę, że ta odpowiedź doskonale to rozwiązuje, wykonując teraz więcej testów. Jeśli tak, to chyba powinienem oznaczyć to pytanie jako dupe, co?
Jonathan Mee
OK, pierwsza rzecz, jaką zauważyłem, nie dostosowuje się wraz z dostosowywaniem ustawień regionalnych. Dlaczego utrzymaniu tsep, place_stri neg_strw ogóle? Dlaczego po prostu nie użyć bezpośrednio fmt_infoczłonków?
Jonathan Mee
OK, numer 2, ten kod nie obsługuje liczb ujemnych ... i nie wiem dokładnie, jak mógłby, while (*ptr-- = *neg_str++)nie ma to dla mnie sensu. Wstawiasz ujemne znaki łańcuchowe w odwrotnej kolejności.
Jonathan Mee
Więc ... wyeliminowałem wyciek pamięci i poprawiłem błąd z liczbami ujemnymi: ideone.com/gTv8Z4 Niestety nadal występuje problem z wieloma separatorami znaków lub wielokrotnymi znakami ujemnymi zapisywanymi w ciągu wstecz. W następnej
kolejności
@JonathanMee: Zaktualizowałem kod (i dodałem co najmniej kilka innych przypadków testowych, w tym liczby ujemne).
Jerry Coffin
2

Matematyczne podejście bez rekurencji lub obsługi ciągów:

Podobnie jak w przypadku rozwiązania rekurencyjnego Paxa, ale obliczając rząd wielkości z wyprzedzeniem, unika się rekursji (być może przy znacznych kosztach).

Zauważ również, że rzeczywisty znak używany do oddzielania tysięcy jest specyficzny dla lokalizacji.

Edycja : zobacz komentarze @ Chux poniżej, aby uzyskać ulepszenia.

Clifford
źródło
1
Zmiana abs(n)na fabs(n)zapobiega błędom komplementu 2 podczas wykonywania print_number(INT_MIN).
chux - Przywróć Monikę
@chux: Słuszna uwaga, ale w wyrażeniu% LHS zostałby rzutowany z powrotem na int i nadal byłby uszkodzony. Być może łatwiej jest po prostu zaakceptować nieznacznie mniejszy zakres dopuszczalnych danych wejściowych lub dodać test i wyjście „-2,147,483,647” bezpośrednio dla INT_MIN (lub cokolwiek INT_MIN znajduje się na omawianej platformie - w tym leży kolejna puszka robaków.
Clifford
Przetestowałem to pomyślnie, zanim zasugerowałem. Hmmm. Widzę, że mój pomysł był przeznaczony tylko dla, log10(abs(n))a nie gdzie indziej. Co ciekawe, twoje rozwiązanie działa z pojedynczą zmianą do log10(fabs(n))iz print_number(INT_MIN)powodu, printf(..., abs(n / order_of_magnitude))co oznacza, n = abs(INT_MIN) % order_of_magnitudeże bycie negatywnym jest w porządku. Jeśli zrezygnujemy z INT_MIN, printf(..., abs(n / order_of_magnitude))może się stać printf(..., n / order_of_magnitude). Ale przypuszczam, że praca z robakiem o nazwie „abs (INT_MIN)” jest zwykle zła .
chux - Przywróć Monikę
Nowa myśl: 3 sugerować zmiany log10(fabs(n)), n = abs(n% order_of_magnitude)i printf(",%03d", n/order_of_magnitude). Przy okazji: nie wydałbym tego wysiłku, chyba że uważam, że twoje rozwiązanie jest dobre. Brak UB, nawet dla INT_MIN.
chux - Przywróć Monikę
2

Oparty na @Greg Hewgill's, ale bierze pod uwagę liczby ujemne i zwraca rozmiar ciągu.

ideasman42
źródło
1

Moja odpowiedź nie formatuje wyniku dokładnie tak, jak na ilustracji w pytaniu, ale może w niektórych przypadkach zaspokoić rzeczywistą potrzebę za pomocą prostego jednowierszowego lub makra. W razie potrzeby można go rozszerzyć, aby wygenerować więcej tysięcy grup.

Wynik będzie wyglądał na przykład następująco:

Value: 0'000'012'345

Kod:

Roland Pihlakas
źródło
Czy 'standardowa notacja jest równoważna ,(przynajmniej matematycznie) w jakiejś części świata?
ysap
1
@ysap W niektórych częściach świata jest to separator tysięcy.
Roland Pihlakas
0

Nie ma naprawdę prostego sposobu na zrobienie tego w C. Po prostu zmodyfikowałbym funkcję int-to-string, aby to zrobić:

Jeremy Ruten
źródło
0

Kolejna funkcja iteracyjna

Johannes Schaub - litb
źródło
Intryguje mnie wyrażenie używane do określenia wymiaru tablicy !? Czy jest na to matematyczne uzasadnienie?
Clifford
ld (10) bitów dla każdej cyfry dziesiętnej. Zaokrąglij w dół do 3. możemy ponownie podzielić 3 (biorąc pod uwagę fakt, że przechowujemy do 3 cyfr jednocześnie). Ale chciałem utrzymać to na górnej granicy.
Johannes Schaub - litb
0

Oto najcieńsza, wydajna pod względem wielkości i szybkości implementacja tego rodzaju formatowania cyfr dziesiętnych:

Użyj w następujący sposób:

Wynik:

Niektóre zalety:

  • Funkcja pobierająca koniec bufora ciągu z powodu odwrotnego uporządkowania formatowania. Wreszcie, gdzie nie ma potrzeby cofania wygenerowanego ciągu (strrev).

  • Ta funkcja tworzy jeden ciąg, którego można użyć w dowolnym algo po. Nie zależy ani nie wymaga wielu wywołań printf / sprintf, co jest strasznie powolne i zawsze zależy od kontekstu.

  • Minimalna liczba operatorów dzielenia (/,%).
Zorg
źródło
Co to jest unlikely?
Dan Bechard,
1
@Dan: unlikelyprawdopodobnie jest wskazówką dla optymalizatora, że ​​warunek prawdopodobnie nie jest prawdziwy. Aby uzyskać więcej informacji, zobacz likely()/ unlikely()macros w jądrze Linuksa .
Jonathan Leffler
@JonathanLeffler Oh, huh. Dzięki za link.
Dan Bechard
0

Bezpieczny format_commas, z liczbami ujemnymi:

Ponieważ VS <2015 nie implementuje snprintf, musisz to zrobić

I wtedy

Przykładowe użycie:

Stefan Steiger
źródło
0

Zmodyfikowana wersja rozwiązania @paxdiablo, ale używająca WCHARi wsprinf:


źródło
0

Jestem nowy w programowaniu w C. Oto mój prosty kod.

K.tin
źródło
0

Moje rozwiązanie wykorzystuje plik. zamiast a, Czytelnik może to zmienić.

Frank Abbing
źródło
0

To jest stare i istnieje wiele odpowiedzi, ale pytanie nie brzmiało „jak napisać procedurę dodawania przecinków”, ale „jak to zrobić w C”? Komentarze wskazywały na ten kierunek, ale w moim systemie Linux z GCC działa to dla mnie:

Po uruchomieniu otrzymuję:

Jeśli wyłączę LC_ALLzmienną przed uruchomieniem programu, unsetenvnie jest to konieczne.

user1683793
źródło
0

Musiałem zrobić coś podobnego sam, ale zamiast drukować bezpośrednio, musiałem przejść do bufora. Oto, co wymyśliłem. Działa wstecz.

Należy pamiętać, że jest przeznaczony tylko dla liczb całkowitych bez znaku i należy upewnić się, że bufor jest wystarczająco duży.

Kranka
źródło
0

Innym rozwiązaniem jest zapisanie wyniku w inttablicy o maksymalnym rozmiarze 7, ponieważ long long inttyp może obsługiwać liczby z zakresu od 9 223 372 036 854 775 807 do -9 223 372 036 854 775 807 . (Zauważ, że nie jest to wartość bez znaku).

Nierekurencyjna funkcja drukowania

główne wywołanie funkcji

testowanie wyjścia

W funkcji main ():

Jeśli potrzebne jest tylko drukowanie, przejdź int numberSeparated[8];do funkcji getNumWcommasi nazwij to w ten sposób getNumWcommas(number).

aah134
źródło
-1

Można to zrobić całkiem łatwo ...

Przykładowe połączenie:

Brian R. Bondy
źródło
-1
Steve Newman
źródło
1
Przynajmniej używaj odpowiedniego wcięcia podczas wysyłania kodu. Być może dodaj też wyjaśnienie, co to robi, czego dotychczasowe odpowiedzi jeszcze nie robią.
EWit
Ma to zaletę prostoty i jest łatwo zrozumiałe na pierwszy rzut oka.
steve newman
1
Fałszywe rozwiązanie, drukuje dodatkowe ,liczby poniżej 100, używa printf()dokąd putchar()poleciałby, używa mylących nazw, chaotycznych wcięć i zdecydowanie za dużo kodu.
chqrlie
-1
Frans van Nispen
źródło
1
Ten kod ma wiele problemów. Niewykorzystana zmienna idxmoże zostać usunięta. Kod nie daje nic dla 0. Nie obsługuje liczb ujemnych. Nie ma żadnego oczywistego powodu, aby buffersię staticzmienną (ogranicza ponowne wejścia kodu). Nie ma wyjaśnienia, co robi, ani nie wspomina się, że po zakończeniu kodu ciąg wskazywany przez pzawiera sformatowany ciąg. Najmniej poważny problem polega na tym, że jako separator tysięcy używa spacji zamiast przecinka. Fakt, że nie obsługuje zera, jest jednak zabójczym problemem.
Jonathan Leffler