[英]Simple, Custom Parsing with c++
我已經讀了一段時間了,但我真的找不到任何幫助我的問題。
我有一個c ++任務來創建一個IAS模擬器。
這是一些示例代碼......
0 1 a
1 2 b
2 c
3 1
10 begin
11 . load a, subtract b and offset by -1 for jump+
11 load M(0)
12 sub M(1)
13 sub M(3)
14 halt
使用c ++,我需要能夠讀取這些行並將它們存儲在我已構建的“內存寄存器”類中...
例如,第一行需要在寄存器0中存儲“1 a”。
如何解析行開頭的數字,然后將其余部分存儲為字符串?
我使用一個使用mem.set(int, string);
調用的類來設置存儲mem.set(int, string);
。 int
是行開頭的內存位置, string
是存儲的指令。
編輯:一些澄清:
我建議看一下<ifstream>
庫。
#include <iostream> // #include <fstream> for file objects as others suggest
#include <string>
#include <map>
using namespace std;
map<int, string> my_program;
int line_num;
string line_text;
while ( cin >> line_num ) { // or any input stream such as a file
getline( cin, line_text ); // standard function defined in <string>
my_program[ line_num ] = line_text; // store line for next phase
}
這將讀取文件的行,直到遇到結束,或者以除數字之外的東西開頭的行。 如果您願意,可以使用cin.eof()
來驗證是否已讀取整個文件。
當然,由於map
對其內容進行排序,因此下一階段的行將按數字順序排列。
如果該行的第一部分始終是數字,請查看strtoul
函數。 從man
頁:
標准C庫(libc,-lc)
#include <stdlib.h>
unsigned long strtoul(const char *restrict str, char **restrict endptr, int base);
strtoul()函數將str中的字符串轉換為unsigned long值。 轉換是根據給定的基數完成的,基數必須介於2和36之間,或者是特殊值0。
該字符串可以以任意數量的空白空間(由isspace(3)
確定)開始,后跟單個可選的+
或-
符號。 如果base
為0或16,則該字符串可以包含0x
前綴,並且該數字將在base 16中讀取; 否則,零基數取10(十進制),除非下一個字符為0
,在這種情況下,它被視為8(八進制)。
字符串的其余部分以明顯的方式轉換為無符號長值,停止在字符串的末尾或在給定基數中不產生有效數字的第一個字符處。 (在10以上的基數中,大寫或小寫的字母A
代表10, B
代表11,依此類推, Z
代表35。)
如果endptr
不為NULL
,則strtoul()
將第一個無效字符的地址存儲在*endptr
。 但是,如果根本沒有數字, strtoul()
會將str
的原始值存儲在*endptr
。 (因此,如果*str
不是\\0
但返回時**endptr
為\\0
,則整個字符串有效。)
strtoul()函數返回轉換結果,或者,如果有前導減號,則返回轉換結果的否定,除非原始(非否定)值溢出; 在后一種情況下, strtoul()
返回ULONG_MAX
。 在所有情況下, errno
都設置為ERANGE
。 如果無法執行轉換,則返回0,並將全局變量errno設置為EINVAL
。
這里的關鍵是endptr
參數。 它設置指向您需要繼續解析的位置。 如果endptr == str
,那么你知道該行沒有以數字開頭。
我喜歡strto___
函數系列比ato__
函數更多,因為你可以設置base
(包括上下文感應“base 0”),因為endptr
告訴我從哪里繼續。 (對於嵌入式應用程序, strto___
占用空間比__scanf
函數小很多。)
編輯:很抱歉錯過您的評論。 要使用endptr
,請編寫如下代碼:
char* restOfLine = NULL;
unsigned long result = strtoul(lineBuffer, 10, &restOfLine);
if(restOfLine == NULL || restOfLine == lineBuffer)
{
/* Handle error. */
}
else
{
// Use result, and do further parsing starting at restOfLine.
}
通常,“handle error”子句返回或中斷或拋出異常或執行其他操作以避免進一步處理,因此您不需要顯式的else
子句。
這樣的東西可以很好地利用Boost.Spirit庫。 它是C ++中的EBNF解析器生成器,如flex和yacc,沒有額外的編譯步驟。
這是簡單的部分:
std::string text_to_parse;
unsigned int register_number;
void Parse_Line(std::istream& data_file)
{
// Read in the register number.
if(data_file >> register_number)
{
// Read the remaining line as a string {variable}
getline(data_file, text_to_parse);
// Now do something with the text...
}
return;
}
您的數據文件存在的問題是它沒有遵循簡單的語法或語法。 例如,您有兩個以11開頭的文本行。第10行不是“內存寄存器”行,而是一條指令行。 此外,第2行和第3行不遵循與0和1相同的語法。
如需更多幫助,請發布語法規則(最好是BNF語法或ASCII藝術)。
分割前導數字和其余部分並不是一項任務太困難。 使用類似getline
東西一次從輸入文件中讀取一行,並將該行存儲在字符串char cur_line[]
。 對於每一行,嘗試這樣的事情:
char* pString
和整數int line_num
strstr
函數查找第一個空白字符,並將結果分配給pString
。 pString
向前移動一個字符,直到它指向非空白字符。 這是包含“其余行”的字符串的開頭。 cur_line
上使用atoi
將字符串中的第一個條目轉換為整數,並將結果存儲在line_num
mem.set(line_num, pString)
解釋這些字符串會變得更加困難,但是......
編輯:正如Mike DeSimone所提到的,如果你使用strto*
函數之一而不是atoi
,你可以結合上面的strstr
和atoi
步驟。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.