Я пытаюсь разобрать строки из текстового файла. Каждая строка представляет собой биржевой тикер и имеет тот же формат, имеющий случайное число строк (для названия компании + символ), за которым следует равномерное количество поплавков (одинаковое количество для каждой строки).
Как бы вы проверили, является ли последняя прочитанная строка плавающей точкой, чтобы узнать, когда я достиг конца строк и начать разбор поплавков?
Примеры строк текста:
A.M. CASTLE & COMPANY CAS 15.71 0.55 3.63 31.57 17.97 8.99 7.79
AAR CORP AIR 17.79 0.19 1.08 30.62 18.45 10.51 38.26
ABBOTT LABORATORIES ABT 45.14 0.01 0.02 -3.24 50.00 40.25 20.33
Образец кода:
void parse(string filename){
ifstream myfile;
string line;
string current_word;
myfile.open(filename);
if (myfile.is_open()){
while (getline(myfile, line))
{
stringstream current_line(line);
while (current_line >> current_word){
// How can I test when I have reached a float here?
}
}
}
}
Вы можете протестировать чтение потока номера, чтобы узнать, удалось ли это сделать, и использовать значение, если оно выглядит следующим образом:
int main()
{
std::string line = "A.M. CASTLE & COMPANY CAS 15.71 0.55 3.63 31.57 17.97 8.99 7.79";
std::istringstream iss(line); // convert the line into a stream
std::string item;
while(iss >> item) // read the stream items (space separated) one by one
{
float f;
if(std::istringstream(item) >> f) // does this item read as a float?
{
// use f here if it does
std::cout << f << " ";
}
}
}
Если вам гарантировано, что ни одна из строк перед поплавком не будет содержать цифру, вы можете просто сравнить каждый символ, пока не будет найдена цифра, и вы нашли первый символ поплавка.
Без этой гарантии я, вероятно, разбирал бы слова. Это тривиальный вопрос о добавлении каждого символа в строку до тех пор, пока не будет найдено пробел. Если эта строка содержит только цифры и один период, то вы нашли свой поплавок. В противном случае перейдите к следующему символу без пробелов и повторите то же самое.
Используйте регулярные выражения, доступные на С++ 11.
Дважды проверьте шаблон чисел с плавающей запятой. Например, мое выражение не позволяет вести знак.
#include <iostream>
#include <ostream>
#include <regex>
#include <sstream>
#include <string>
int main()
{
std::istringstream input("A.M. CASTLE & COMPANY CAS 15.71 0.55 3.63 31.57 17.97 8.99 7.79");
// Pattern for recognizing floating-point numbers
std::regex pattern(R"(\d+\.(\d*)?((e|E)(\+|\-)?\d+)?)");
for (std::string line; std::getline(input, line); )
{
// We have a successful read of one line
// Now extract the floating-point numbers on that line
auto first = std::sregex_iterator(line.cbegin(), line.cend(), pattern);
auto last = std::sregex_iterator();
for (; first != last; ++first)
{
double d = std::stof(first->str());
std::cout << d << std::endl;
}
}
return 0;
}
Вы можете использовать sscanf()
, который выполнит запрошенную операцию в одной строке.
bool parseNameAndFloats(char const *input, char *name, unsigned int namesize, float *floatArray, unsigned int floatsize)
{
// Assuming the sample string is representative, there 7 floats in it.
if (floatsize < 7)
{
return false;
}
char *temp = strdup(input);
if (temp == NULL)
{
// deal with allocation failure in strdup;
return false;
}
int count = sscanf(input, "%[^0-9.]s %f %f %f %f %f %f %f", temp, floatArray, floatArray + 1, floatArray + 2, floatArray + 3, floatArray + 4,floatArray + 5, floatArray + 6);
if (namesize > 0)
{
strncpy(name, temp, namesize);
name[namesize - 1] = 0;
}
free(temp);
return count == 8;
}
Есть те, которые будут критиковать sscanf()
, а при неправильном использовании это может вызвать некоторые серьезные проблемы. Одной из таких причин я использую strdup()
чтобы сделать копию исходной строки ввода. Это гарантировало получение мною буфера, достаточно большого, чтобы удерживать результаты преобразования %[^0-9.]s
S. Затем я использую strncpy()
для извлечения не более, чем поместится в предоставленный буфер, и обязательно завершите NUL.