![]() |
Заполнение массива в С++ Builder из файла "txt"
Существует текстовый файл "file.txt" следующего вида:
Цитата:
Цитата:
|
не сильно заботясь об быстроте:
Код:
// создаем список |
spike, прости меня дурака, но я не всё понял. Разъясни, пожалуйста...
Я понял, что при открытии файла мы заполняем список. Затем пробегаем по каждой строке списка и удаляем содержимое до знака ";". Тем самым удаляем содержимое первых 2 столбцов. А вот что значит строчка Цитата:
Потом мы зачем-то стираем и третий столбец. Цитата:
Строка Цитата:
Когда-то я неплохо программил на Pascal'е и Borland C++, но с тех пор прошло ОЧЕНЬ много времени и я без практики всё позабыл. И сейчас глядя на этот код чувствую себя первокласником... :( Может я конечно совсем обнаглел так наседая, но мне очень нужно в этом разобраться, поэтому очень прошу твоей помощи!!! :молись: |
Ну, если уж делать "как положено", то есть с привлечением средств ООП, то нужно создать класс, описывающий формат файла и умеющий считывать его в память (опционально - записывать содержимое памяти обратно в файл того же формата) с функцией-членом, обеспечивающей чтение произвольного элемента.
И при заполнении целевого массива использовать эту функцию со сдвигом на 2 столбца (т.е. 1-й элемент строки целевого массива считывается считывается из 3-го элемента соответствующей "строки" класса). А всю "прозу жизни" (разбор строк файла) упрятывать в этот класс. Если количество строк сильно переменное (скажем, от 10 до 10000) имеет смысл целевой двумерный массив заменить на связанный список указателей на элементы-"строки" (чтобы не выделять априорно лишнюю память и в то же время не оказаться в ситуации, когда выделенная память недостаточна для считывания файла). В общем, очень многое зависит от того, что в дальнейшем планируется делать с целевым массивом... Цитата:
На самом деле, данная функция должна стоять в правой части оператора присваивания... ;) Цитата:
Цитата:
Цитата:
|
Borland, "+1" это я ошибся, не надо
|
Borland, прошу прощения за свою назойливость, но не мог бы ты привести хотя бы примерный код? Я в классах вообще не разбираюсь. Только вчера удалось найти книгу, в которой рассказывается про классы и наследования.
Цитата:
Цитата:
|
В конечном итоге с классами не разобрался. Массив заполнил использовав совет spike'а. Только немного изменил код, подогнав под свои нужды.
Сейчас возникла другая трудность. В программе участвуют 10 массивов одинакового размера, со всеми ними будут производиться одни и те же действия, поэтому пришёл к выводу, что нужно написать функцию, как в старом добром "Borland C++" (не Builder), и вызывать её для каждого массива. Для примера: Код:
void vvodmass (int mass[5][5]) //объявляем функцию Код:
__fastcall TForm1::TForm1(TComponent* Owner) Цитата:
P.S. Если забыть про остальные массивы, и прописать процесс рисования не как функцию, то диаграмма рисуется просто замечательно. |
у тебя функция diagramma не является методом класса TForm1 и соответственно не может юзать внутренние объекты , коим является Image1
нужно либо функцию определить методом класса: в Unit1.h там где "public: //User decloration" прописать прототип функции, а при описании функции указать void TForm1::diagramma(...), либо передавать в функцию указатель на объект на ряду с остальными параметрами |
С классами я уже пробовал разбираться, поэтому отбрасываем этот вариант. А что значит передавать в функцию указатель на объект? Везде прописывать "Form1->Image1->..."? Можно хотябы маленький пример для наглядности?
|
да, так как ты написал тоже сработает в данном конкретном примере, но это имхо не хорошо, делать свои функции зависимыми от глобальных переменных
я говорил о том, что в функцию передавать указательна объект, то есть прототип функции будет выглядеть так: void diagramma(int mass[37][5], TImage *Image1) а вызывать ее соответственно вот так: diagramma (mass1, Image1); опять же в твоем конкретном случае, потому что функция diagramma вызывается из метода класса TForm1, где Image1 определен да и с классом все просто: в нижней панели окна кода находим закладки, среди них unit1.h, переходим туда и в public, там где определяется пользователем пишем void diagramma(int mass[37][5]); а в основном коде (cpp - файл) определяем прототип как void TForm1::diagramma(int mass[37][5]) метод выбирай на свой вкус, судить о правильности и корректности я не буду (ну не считая первого случая, но там мое имхо) |
Очередная проблема: Создаю "Form2" и заполняю на ней два поля "Edit". После закрытия этой формы нужно вернуть значения этих полей. Но командра "return" не желает работать. Выдаётся ошибка:
Цитата:
Код:
void __fastcall TForm2::Button1Click(TObject *Sender) |
Это ж самые основы программирования!!!
return ВСЕГДА возвращает ОДНО значение. К тому же, насколько я понимаю описание твоей функции, она вообще не может возвращать значений ибо void (что вполне логично для обработчика клика). Если нужно вернуть несколько значений - можно, например, изменять параметры, но для этого в Си необходимо передавать их в функцию по ссылке или указателю. Или возвращаемым значением функции делать экземпляр класса, содержащий необходимые значения внутри с соответствующей модификацией самой функции (хотя в качестве обработчика клика этот подход не годится). |
Цитата:
К тому же, я прописал функцию рисования диаграммы, как сказал PhoeniX (мне нужна была функция, потому что придётся постоянно рисовать диаграмму, но с разным масштабом/с разных массивов), но вызвать её могу только при нажатии на "Button1", потому что я объявил массивы именно при нажании на эту кнопку. Код:
void __fastcall TForm1::Button1Click(TObject *Sender) Цитата:
P.S. Извините, что задаю так много вопросов и часто - очень глупых, но мне очень нужна ваша помощь. :help::молись: Заранее ПРЕМНОГО благодарен за все ответы и советы. Практически все предыдущие советы мне очень помогли. |
А не проще воспользоваться isdigit()?
|
В общем выяснил как объявлять переменные глобально и объявил соответствующим способом все 10 массивов и нужные для работы переменные. Программа отлично заработала. Теперь мне интересен другой вопрос: У меня в программе есть поток событий:
Код:
{import (mass1); |
Emelman, Form2->ShowModal();
|
ЕЖ, то, что надо! :yees:
Возникла очередная интересная проблемка: Существует каталог, где расположен exe'шник программы и внутри него папка Base, в которой хранятся текстовые файлы с вышеописанными данными. В программе присутствует 2 кнопки: одна - импорт файла папки "Base", а другая вызывает OpenDialog. Если сначала импортировать файлы из папки "Base" (путь прописан "Base/<имя текстового файла>"), то всё работает "на Ура". Но стоит нажать другую кнопку и указать текстовый файл из левого каталога, то видимо рабочий каталог меняется на последний используемый, и из базы данных уже ничего не получить (<левый каталог>/Base/<имя текстового файла> - не найдено). В идеале было бы не плохо сначала определить путь текущего каталога (где находится exe'шник) и запонимть его (пускай даже в переменной типа String). А перед нажатием на кнопку импортирования файла из папки "Base" изменить путь рабочего каталога на изначальный. Но я нигде не могу найти существуют-ли команды определения и смены рабочего каталога. Помогите, пожалуйста! |
Смотри свойства/функции Application->ExeName / ExtractFilePath() / GetCurrentDir()
При импорте просто используй абсолютный путь, что то типа ExtractFilePath(Application->ExeName) + твои файлы из Base |
Path:=Application.ExeName; // полный путь и название запущенной программы
Path:=ExtractFileDir(Path); // отбрасываем название программы. Остается путь. if Path[Length(Path)]<>'\' then Path:=Path+'\'; // если последний символ не \ то добавить его {Последняя строка нужна для присвоения последнего символа '\', потому что при обрезке файла 'C:\PROG.EXE' получим 'C:\', а при обрезке 'C:\WINDOWS\PROG.EXE' получим 'C:\WINDOWS'} |
WISE, зачем? Есть ExtractFileDir, а есть ExtractFilePath - разница как раз в том, что ExtractFileDir не оставляет последнего символа '\', а ExtractFilePath оставляет.
|
Я решил не заморачиваться с файлами и загнал все данные в базу данных. Так, возможно, и работать быстрее будет. А информация насчёт путей всё равно полезная. Тем более, что в интернете по этому вопросу практически ничего нет.
|
Вроде бы всё работает, разве что напрягает, что данные записываются в конец базы данных ("Append();"). Чтобы данные записывались сразу по сортировке хотел написать следующий код:
Код:
Table1->First(); |
сравнение должно работать со строками (как я понимаю тут AnsiString)
к сожалению, из приведенного куска кода не ясно что такое author1 и author2, но предполагаю, что в сравнении и присвоении все же должна быть одна и та же переменная :idontnow: проверь это, может все и дело в том что не с той переменной сравниваешь? |
PhoeniX, :yees: :ржать:
Вторые сутки коверкаю код как могу, и за всё это время не увидел опечатки!!! Когда же я прекращу тупить?... Спасибо за замечание! :biggrin: |
А можно ли отучить C++ различать регистр при сравнении? А то на данный момент получается, что "Z" (Зет большое)<"a"(А малое).
|
сравнивай в верхнем регистре (должна быть функция ToUpper или чтото вроде того)
|
Цитата:
|
Цитата:
Цитата:
Во всяком случае на данный момент сортированность данных не нарушается, и это для меня самое главное. Единственное, что мне сейчас не нравится - что при данном написании кода "Толстой" (с большой буквы) записывается в базу перед "достоевским" (с маленькой буквы). Т.е. при сравнении строк идёт проверка в следующем порядке: сначала идут заглавные латинские буквы "A-Z", затем прописные латинские буквы "a-z", затем идут заглавные русские буквы "А-Я" и уже в конце идут прописные русские буквы "а-я". Мне же хотелось, чтобы заглавные и прописные буквы расценивались одинаково. Т.е. на данный момент записи хранятся в следующем виде: Цитата:
Цитата:
|
Emelman, нельзя обсуждать эти вопросы не зная конкретную используемую СУБД. Тут надо думать не на уровне программы, а на уровне СУБД.
Если это какая-то элементарная файловая БД типа .dbf, то вполне возможно что ее драйвер позволяет физически втиснуть новую запись между двумя старыми, при этом, что естественно, ему придется физически перечитать и перезаписать со сдвигом на диск всю часть файла-БД после этой новой записи, что несет большие накладные расходы. В серьезных же СУБД, типа MS SQL Server / Oracle такого не происходит. Единственный язык, который они понимают - это SQL, который для записи новых данных использует только одну команду - INSERT, без всякого указания места, куда эту запись вставить. Только сервер решает куда она физически будет записана, и это вполне может оказаться вообще другой диск, если так сконфигурирована база. Соответственно при следующей выборке данных трудно говорить о порядке и полагаться на что-то. Для достижения нужного результата лучше самому указывать сортировку по некому признаку. Что касается сортировки по умолчанию и сравнения строчных и прописных букв - это опять же настройки collation конкретного драйвера/СУБД, хотя указывая сортировку в SQL-запросе всегда можно задать и свои условия, что-то типа ORDER BY UPPER(Name). |
Я писал данную программу с вашей помощью и помощью нескольких книг типа "C++ Builder для начинающих". Как в книгах было написано про простую базу данных, так я её и создал. А именно: Запустил "BDE Administrator" и создал в нём базу данных с характеристиками:
Цитата:
|
Помогите"SOS"
Мне нужно найти количество положительных чисел в каждом столбце 2х массива. Заранее спасибо. |
Часовой пояс GMT +4, время: 05:39. |
Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.