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.


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).


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:


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.


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.


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).


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.


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.


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:


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ć:


0

Kolejna funkcja iteracyjna


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 (/,%).

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:


0

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


0

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


0

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


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.


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.


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).


-1

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

Przykładowe połączenie:


-1

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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.