Can someone explain me this unexpected output?

I was trying to handle different line-ending.
But as I expected the output was different than expected.
test4utf8mac.srt is a subtitle file of SubRip format having line-ending of the CR type(i.e '\r').
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int32_t main()
{
    #define NLINE "Default"
    string str;
    string delimiter;
    std::ifstream chk("./srt/test4utf8mac.srt",ios::binary);
    if(!chk.is_open())
    {
        cout<<"file not opened"<<endl;
        return 0;
    }
    cout<<"1) "<<NLINE<<endl;
    if(getline(chk, str)) 
    {
        if(str.size() && str[str.size()-1] == '\r') 
        {
            delimiter = "\\r";
            #undef NLINE
            #define NLINE "\\r"

            cout<<"2) "<<NLINE<<endl;
        }
        else if(str.size() && str[str.size()-1] == '\n')
        {
            cout<<"3) "<<NLINE<<endl;
            if(str[str.size()-2] == '\r') 
            {
                cout<<"3.1) "<<NLINE<<endl;
                delimiter = "\\r\\n";
                #undef NLINE
                #define NLINE "\\r\\n"
            }
            else
            {
                cout<<"3.2) "<<NLINE<<endl;
                delimiter = "\\n";
                #undef NLINE
                #define NLINE "\\n"
            }
        }
        cout<<"4) "<<NLINE<<endl;
    }
    cout<<"Delimiter: "<<delimiter<<endl<<"Final: "<<NLINE;
}


Original Output:
1) Default
2) \r
4) \n
Delimiter: \r
Final: \n

Expected Output:
1) Default
2) \r
4) \n
Delimiter: \r
Final: \r

Why is my MACRO changing to \n isn't it supposed to be \r.
The preprocessor macros are processed before the compiler set in. I.e. the last #define will win (line 42).

#define will not change at runtime.
you sound like you know what you are doing but there are 5 or so line endings in generic text files. see the table here if you need to support them all. https://en.wikipedia.org/wiki/Newline#Representation
you can probably ignore the old qnx one. Anyone else remember the old qnx bootable floppy toy?
Last edited on
You could evaluate this at compile time without "macro-fying" things by using a if constexpr check instead (hugely more helpful with messages catching ill-formed expressions instead of the sometimes not-so-helpful messages if you screw something up with macros). If you needed to keep track of line endings throughout your program and if those line endings are changing, I would recommend just wrapping the known line endings into an enum and unordered map:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum class Platform{
    unix = 0,
    windows = 1,
    mac = 2,
    //etc
};

std::unordered_map<Platform, const char*> line_endings{
{
    {unix, "\\n"} ,
    {windows, "\\r\\n"}, 
    {mac, "\\r"}, 
     //etc
};


which would allow you to do a runtime check similar to what you're doing and just indexing into the map with the platform type.
i.e.
1
2
3
4
5
 if(str.back() == '\r') 
        {
            delimiter = mac;
            // do whatever you need to do to the file/store delimiter and file here
        }...


1
2
3
4
5
6
7
 
... 
switch(delimiter){
    case Platform::mac:
        fileLine.append(line_endings.at(mac));
    break;
...



You could then store that delimiter to use, although, that delimiter storage only helps if you're processing one file at a time in this context.
If you're processing multiple files at once, you could use something like std::pair to couple the file and delimiter together, store them in something like a vector, and use structured bindings to retrieve those values later to deal with the line endings (although you would need a way to match the file correctly in this case).
If you did store the delimiter with the file (assuming multiple files) then it becomes easy to handle the file if you needed to say, append more to that file in the same format. Converting file types is a bit more involved.

It all depends on your use case and intent really. As far as I'm aware, there's a multitude of ways to go about this - but as coder777 stated
The preprocessor macros are processed before the compiler set in...#define will not change at runtime.
so your runtime evaluation really doesn't alter anything here

I hope you get a solution that answers your question better (I'm a hobby self-taught programmer so my approaches tend to not be a part of the "well known and tested" thought processes, but just wanted to give some insight in case it might have been helpful lol )

As an example of what you may use this approach with:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
if(getline(chk, str))  {
    auto eol { str.back() };
    if( eol == '\r'){
        delimiter = Platform::mac;
    } else if (eol == '\n') {
        delimiter = Platform::unix;
    } else if (eol == '\r\n') {
        delimiter = Platform::windows;
    }
}

...

if(delimiter == windows){
    delimiterSize = 2;
} else {
    delimiterSize = 1;
}
while(getline(chl, str) {
    str.erase(str.rbegin() + str.rbegin() + delimiterSize);
    // pretending you're storing these lines in a container
    fileLines.emplace_back(str);
} // while loop

...

// convert windows line endings to mac for instance
for( auto & line : fileLines) {
    line.append(line_endings.at(Platform::mac));
}
// then chunk the data for easier writes to another file or just append 
// each line to one large string and write that large string in one write call 

Last edited on
Topic archived. No new replies allowed.