Я писал проект кода C++ для школы, который хочет, чтобы я прочитал текстовый файл из аргумента командной строки и вывел его вместе с добавлением прилагательных, объявленных в командной строке после любых статей [a, an, the]. Это первый раз, когда я должен использовать командную строку для любого проекта.
Мне удалось получить и прочитать текстовый файл из командной строки, но моя проблема в том, что когда я хочу получить строку в качестве параметра функции isArticle(), я продолжаю получать следующее сообщение в оболочке UNIX:
./test-program1[38]: eval: line 1: 7704: Memory fault
0a1,4
Я не подозреваю, что проблема isArticle()
функцией isArticle()
но вот она:
bool isArticle(string n)
{
string article[]={"a","an","the","A","An","aN","AN","The","tHe","thE","THe","tHE","ThE","THE"};
for(int i=0;i<sizeof(article);i++)
{
if(n.compare(article[i])==0)
return true;
}
return false;
}
Хотя он не завершен, вот какой-то тестовый код, который я использую, чтобы увидеть, работает ли функция isArticle():
int main(int argc, char *argv[])
{
istream *br;
ifstream file;
if(argc == 1)
br = &cin;
else if(argc==3)
{
file.open(argv[2]);
if(file.is_open())//put work here
{
br=&file;
string word;
string output[sizeof(file)];
while(file>>word)
{
if(isArticle(word)==true)
{
cout<<word;
}
}
}
else
{
usage(argv[2],"Cannot open "+string(argv[2]));
return 1;
}
}
else
{
usage(argv[1], "More than one filename was given");
return 1;
}
return 0;
}
Оператор sizeof
не возвращает количество элементов в массиве. Он возвращает пространство памяти, занимаемое массивом. Массив char
будет иметь другой sizeof
чем массив int
.
Для вашего массива вы можете определить количество элементов во время компиляции:
const unsigned int elements_in_array =
sizeof(article) / sizeof(article[0]);
sizeof(array) / sizeof(array[0])
; это опасно. Если массив распался на указатель, он все равно скомпилируется, но не будет делать то, что вы хотите. C ++ 11 имеет std::begin
и std::end
для этого; до C ++ 11, конечно, у всех нас было что-то эквивалентное в нашем наборе инструментов.
Как уже указывали другие, sizeof
не дает количество элементов в массиве; идиоматический способ сделать это - использовать std::end( article ) - std::begin( article )
. В pre- C++ 11 большинство опытных программистов C++ сделали бы то же самое, используя версии begin
и end
своего инструментария.
Вся isArticle
функция весьма unidiomatic. В C++ 11 вся функция будет одним вызовом библиотеки:
bool
isArticle( std::string const& word )
{
static std::string const articles[] = {
"a", "an", "the", "A", "An", "aN", "AN",
"The", "tHe", "thE", "THe", "tHE", "ThE", "THE",
};
return std::any_of(
std::begin( articles ), std::end( articles ),
[=]( std::string const& target ) { return word == target; } );
}
В предыдущем C++ мы бы написали:
return std::find( begin( articles ), end( articles ), word )
!= end( articles );
(с самого begin
и end
нашего обычного инструментария). И если бы мы хотели (по педагогическим причинам) написать цикл самим, это было бы что-то вроде:
std::string::const_iterator current = std::begin( articles );
std::string::const_iterator end = std::end( articles );
while ( current != end && *current != word ) {
++ current;
}
return current != end;
Несколько вопросов, которые не связаны с вашей непосредственной проблемой, вероятно, заслуживают упоминания:
Типы классов обычно передаются по ссылке на const, а не по значению. Это, возможно, преждевременная оптимизация, но это настолько повсеместно, что все остальное заставляет задуматься, почему.
Значения, которые не изменяются в функции, должны быть объявлены как const
и static
.
std::string
поддерживает ==
и !=
; если вы ищете равенство, то что вы должны использовать. Функция compare
действительно должна использоваться только для лексикографического упорядочения.
Возвращение из середины цикла - это то, чего вы обычно хотите избежать. Конечно, когда функции просты, это действительно не имеет значения, но это плохая привычка.
Это касается только рассматриваемой функции. В main
вас также есть проблема с sizeof
. В этом случае, похоже, вы пытаетесь использовать его для определения количества слов в файле. Это невозможно сделать без фактического чтения файла. Здесь вам понадобится std::vector<std::string>
, а не массив стиля C (размер которого должен быть известен во время компиляции).
И, конечно же, тип, необходимый для if
является bool
. isArticle
возвращает bool
, поэтому больше ничего не нужно. Написание isArtile( word ) == true
настоятельно указывает на то, что вы не знаете, что такое тип bool
. (Подсказка: тип выражения isArtile( word ) == true
также bool
.)
И одно последнее предложение: если в программе нет аргументов, вы ничего не делаете. Я не думаю, что это намерение. Обычное решение для процессов командной строки под Unix (а также широко распространенное под Windows) заключается в том, чтобы поместить всю фактическую работу в функцию и написать что-то вроде:
int
main( int argc, char** argv )
{
if ( argc == 1 ) {
process( std::cin );
} else {
for ( int i = 1; i != argc; ++ i ) {
std::ifstream in( argv[i] );
if ( ! in ) {
// Output error message and set global flag for
// return value...
} else {
process( in );
}
}
}
return globalFlagWithReturnValue;
}
Функция process
занимает std::istream&
в качестве аргумента, что позволяет ему читать либо std::cin
или открыт std::istream
.
ostream
, но в последующем предложении говорится об istream
. Что это?
istream
производные std::cin
и std::ifstream
(так как это входные данные). Я исправил это. Благодарю.
sizeof(article)
иsizeof(file)
(а не то, что вы думаете.)sizeof(article)
будет намного больше 14, но именно столько строк определено в массиве. Хорошо, что вы включилиisArticle()
так как это является источником проблемы, так что спасибо, что включили ее. Когда у вас есть определение массива в подобной функции, количество элементов в массиве равноsizeof(article) / sizeof(article[0])
. Это не работает для параметров массива для функций; тогда вызывающему коду необходимо явно передать размер.