Jak zadeklarować tablicę ciągów w C ++?

89

Staram się iterować wszystkie elementy statycznej tablicy ciągów w najlepszy możliwy sposób. Chcę móc zadeklarować go w jednej linii i łatwo dodawać / usuwać z niego elementy bez konieczności śledzenia liczby. Brzmi naprawdę prosto, prawda?

Możliwe nierozwiązania:

vector<string> v;
v.push_back("abc");
b.push_back("xyz");

for(int i = 0; i < v.size(); i++)
    cout << v[i] << endl;

Problemy - brak możliwości stworzenia wektora w jednej linii z listą łańcuchów

Możliwy brak rozwiązania 2:

string list[] = {"abc", "xyz"};

Problemy - nie ma możliwości automatycznego uzyskania liczby ciągów znaków (o której wiem).

Musi być na to łatwy sposób.

naumcho
źródło
Biblioteka Boost przypisać wydaje się być dokładnie to, czego szukasz. Dzięki temu przypisywanie stałych do kontenerów jest łatwiejsze niż kiedykolwiek.
Craig H

Odpowiedzi:

108

C ++ 11 dodał listy inicjalizacyjne, aby umożliwić następującą składnię:

std::vector<std::string> v = {"Hello", "World"};

Obsługa tej funkcji języka C ++ 11 została dodana w co najmniej GCC 4,4 i tylko w programie Visual Studio 2013 .

Anthony Cramp
źródło
2018. Właśnie zacząłem C ++ i przeprowadziłem sporo badań dotyczących elastycznych tablic. Skończyło się na użyciu wektorów ...
Robert Molina
37

Możesz zwięźle zainicjować a vector<string>z char*tablicy utworzonej statycznie :

char* strarray[] = {"hey", "sup", "dogg"};
vector<string> strvector(strarray, strarray + 3);

Nawiasem mówiąc, to kopiuje wszystkie ciągi, więc zużywasz dwukrotnie więcej pamięci. Możesz skorzystać z sugestii Willa Deana, aby zastąpić magiczną liczbę 3 tutaj arraysize (str_array) - chociaż pamiętam, że był jakiś specjalny przypadek, w którym ta konkretna wersja Arraysize może zrobić coś złego (przepraszam, nie pamiętam szczegółów od razu) . Ale bardzo często działa poprawnie.

Ponadto, jeśli naprawdę zależy Ci na jednej linii, możesz zdefiniować makro wariadyczne, tak aby pojedyncza linia DEFINE_STR_VEC(strvector, "hi", "there", "everyone");działała.

Tyler
źródło
Ponieważ strarrayznajduje się w nagłówku, czy nie naruszy to reguły jednej definicji?
jww
22

Problemy - nie ma możliwości automatycznego uzyskania liczby ciągów znaków (o której wiem).

Istnieje na to standardowy sposób, w którym wiele osób (w tym MS) definiuje makra, takie jak arraysize:

#define arraysize(ar)  (sizeof(ar) / sizeof(ar[0]))
Will Dean
źródło
1
Alternatywnie, można użyć czegoś takiego: template<typename T, size_t N> inline size_t arraysize(T (&ar)[N]) { return N; } (Inline nie słowa kluczowego konieczne, ale używane w celu udokumentowania danej funkcji zamiar nowoczesny kompilator powinien teoretycznie być w stanie powrócić całą funkcję, wierzę..
Justin Time - dozbrojenie Monica
1
To się nie powiedzie dla wskaźników. Liczenie elementów tablicy powinno odbywać się w C ++ inaczej.
jww
8

Zadeklaruj tablicę ciągów w C ++ w następujący sposób: char array_of_strings[][]

Na przykład : char array_of_strings[200][8192];

pomieści 200 ciągów, z których każdy ma rozmiar 8kb lub 8192 bajtów.

służy strcpy(line[i],tempBuffer); do umieszczania danych w tablicy ciągów.


źródło
FYI, char array_of_strings [] [] nie może akceptować ciągów C ++, pamiętaj, aby najpierw przekonwertować na znak *. cplusplus.com/reference/string/string/c_str
Luqmaan
Ponieważ array_of_stringsznajduje się w nagłówku, czy nie naruszy to reguły jednej definicji?
jww
7

Jedną z możliwości jest użycie wskaźnika NULL jako wartości flagi:

const char *list[] = {"dog", "cat", NULL};
for (char **iList = list; *iList != NULL; ++iList)
{
    cout << *iList;
}
Zaćmienie
źródło
Co właściwie oznacza char **? Czy w javie byłaby to lista ciągów?
IAmGroot
1
@Doomsknight: W tym przypadku tak. W pierwszym wierszu definiuję tablicę char*. W pamięci są to 3 wskaźniki - jeden punkt na „psa”, jeden na „kota”, a drugi na NULL. Mogę wziąć wskaźnik do pierwszego wskaźnika i uzyskać char**- wskaźnik do wskaźnika do znaku. Kiedy to zwiększam, przesuwam znak **, aby wskazywał na następny element na liście - wskaźnik do wskaźnika, który wskazuje na „kot”, następnie ponownie zwiększam wartość i otrzymuję wskaźnik wskazujący na wskaźnik NULL i Wiem, że skończyłem. (
Eclipse,
4

Możesz użyć funkcji begini endz biblioteki zakresów Boost, aby łatwo znaleźć końce pierwotnej tablicy, iw przeciwieństwie do rozwiązania makr, spowoduje to błąd kompilacji zamiast zepsutego zachowania, jeśli przypadkowo zastosujesz go do wskaźnika.

const char* array[] = { "cat", "dog", "horse" };
vector<string> vec(begin(array), end(array));
Ross Smith
źródło
3

Możesz użyć sugestii Willa Deana [ #define arraysize(ar) (sizeof(ar) / sizeof(ar[0]))], aby zastąpić magiczną liczbę 3 tutaj arraysize (str_array) - chociaż pamiętam, że był jakiś specjalny przypadek, w którym ta konkretna wersja arraysize może zrobić coś złego (przepraszam, nie pamiętam szczegółów natychmiast). Ale bardzo często działa poprawnie.

Sytuacja, w której to nie działa, ma miejsce, gdy „tablica” jest tak naprawdę tylko wskaźnikiem, a nie rzeczywistą tablicą. Ponadto, ze względu na sposób, w jaki tablice są przekazywane do funkcji (konwertowane na wskaźnik do pierwszego elementu), nie działa między wywołaniami funkcji, nawet jeśli podpis wygląda jak tablica - some_function(string parameter[])tak naprawdę some_function(string *parameter).

Matthew Crumley
źródło
3

Oto przykład:

#include <iostream>
#include <string>
#include <vector>
#include <iterator>

int main() {
    const char* const list[] = {"zip", "zam", "bam"};
    const size_t len = sizeof(list) / sizeof(list[0]);

    for (size_t i = 0; i < len; ++i)
        std::cout << list[i] << "\n";

    const std::vector<string> v(list, list + len);
    std::copy(v.begin(), v.end(), std::ostream_iterator<string>(std::cout, "\n"));
}
Cień2531
źródło
2

Zamiast tego makra mógłbym zasugerować to:

template<typename T, int N>
inline size_t array_size(T(&)[N])
{
    return N;
}

#define ARRAY_SIZE(X)   (sizeof(array_size(X)) ? (sizeof(X) / sizeof((X)[0])) : -1)

1) Chcemy użyć makra, aby uczynić z niego stałą czasu kompilacji; wynik wywołania funkcji nie jest stałą czasu kompilacji.

2) Jednak nie chcemy używać makra, ponieważ mogłoby to zostać przypadkowo użyte na wskaźniku. Ta funkcja może być używana tylko w tablicach kompilowanych w czasie.

Dlatego używamy zdefiniowanej funkcji, aby uczynić makro „bezpiecznym”; jeśli funkcja istnieje (tj. ma niezerowy rozmiar), używamy makra jak powyżej. Jeśli funkcja nie istnieje, zwracamy złą wartość.

DrPizza
źródło
2
#include <boost/foreach.hpp>

const char* list[] = {"abc", "xyz"};
BOOST_FOREACH(const char* str, list)
{
    cout << str << endl;
}

źródło
1
#include <iostream>
#include <string>
#include <vector>
#include <boost/assign/list_of.hpp>

int main()
{
    const std::vector< std::string > v = boost::assign::list_of( "abc" )( "xyz" );
    std::copy(
        v.begin(),
        v.end(),
        std::ostream_iterator< std::string >( std::cout, "\n" ) );
}
Dominic.wig
źródło
1

Możesz bezpośrednio zadeklarować tablicę ciągów, taką jak string s[100];. Następnie, jeśli chcesz uzyskać dostęp do określonych elementów, możesz to uzyskać bezpośrednio jak s[2][90]. Na potrzeby iteracji weź rozmiar łańcucha za pomocą s[i].size()funkcji.

kajol jain
źródło